/*******************************************************************************
 * Copyright (c) 2008 Scott Stanchfield.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Based on the ANTLR parser generator by Terence Parr, http://antlr.org
 *   Ric Klaren <klaren@cs.utwente.nl>
 *   Scott Stanchfield - Modifications for XML Parsing
 *******************************************************************************/
package com.javadude.antxr;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.javadude.antxr.actions.java.ActionLexer;
import com.javadude.antxr.collections.impl.BitSet;

/**Generate MyParser.java, MyLexer.java and MyParserTokenTypes.java */
public class JavaCodeGenerator extends CodeGenerator {
    // non-zero if inside syntactic predicate generation
    /** Constant to indicate that we shouldn't generate a mapping entry */
    public static final int NO_MAPPING = -999;
    /**
     * Constant to indicate that we should continue mapping based on the
     *   last mapping seen by the SMAP generator (there may have been
     *   intermediate mappings generated by nested elements)
     */
    public static final int CONTINUE_LAST_MAPPING = -888;

    private JavaCodeGeneratorPrintWriterManager printWriterManager;
    private int defaultLine = JavaCodeGenerator.NO_MAPPING;

    protected int syntacticPredLevel = 0;

    // Are we generating ASTs (for parsers and tree parsers) right now?
    protected boolean genAST = false;

    // Are we saving the text consumed (for lexers) right now?
    protected boolean saveText = false;

    // Grammar parameters set up to handle different grammar classes.
    // These are used to get instanceof tests out of code generation
    String labeledElementType;
    String labeledElementASTType;
    String labeledElementInit;
    String commonExtraArgs;
    String commonExtraParams;
    String commonLocalVars;
    String lt1Value;
    String exceptionThrown;
    String throwNoViable;

    /** Tracks the rule being generated.  Used for mapTreeId */
    RuleBlock currentRule;

    /** Tracks the rule or labeled subrule being generated.  Used for
     AST generation. */
    String currentASTResult;

    /** Mapping between the ids used in the current alt, and the
     * names of variables used to represent their AST values.
     */
    Map<String, String> treeVariableMap = new HashMap<String, String>();

    /** Used to keep track of which AST variables have been defined in a rule
     * (except for the #rule_name and #rule_name_in var's
     */
    Map<AlternativeElement, AlternativeElement> declaredASTVariables = new HashMap<AlternativeElement, AlternativeElement>();

    /* Count of unnamed generated variables */
    int astVarNumber = 1;

    /** Special value used to mark duplicate in treeVariableMap */
    protected static final String NONUNIQUE = new String();

    /** maximum number of cases in a generated switch */
    public static final int caseSizeThreshold = 127; // ascii is max

    private List<String> semPreds;

    /** Create a Java code-generator using the given Grammar.
     * The caller must still call setTool, setBehavior, and setAnalyzer
     * before generating code.
     */
    public JavaCodeGenerator() {
        super();
        charFormatter = new JavaCharFormatter();
    }

    @Override
    protected void printAction(String code) {
        printAction(code, defaultLine);
    }
    protected void printAction(String code, int sourceStartLine) {
        getPrintWriterManager().startMapping(sourceStartLine);
        super.printAction(code);
        getPrintWriterManager().endMapping();
    }

    @Override
    public void println(String code) {
        println(code, defaultLine);
    }
    public void println(String code, int sourceStartLine) {
        if (sourceStartLine > 0 || sourceStartLine == JavaCodeGenerator.CONTINUE_LAST_MAPPING) {
            getPrintWriterManager().startSingleSourceLineMapping(sourceStartLine);
        }
        super.println(code);
        if (sourceStartLine > 0 || sourceStartLine == JavaCodeGenerator.CONTINUE_LAST_MAPPING) {
            getPrintWriterManager().endMapping();
        }
    }

    @Override
    protected void print(String code) {
        print(code, defaultLine);
    }
    protected void print(String code, int sourceStartLine) {
        if (sourceStartLine > 0 || sourceStartLine == JavaCodeGenerator.CONTINUE_LAST_MAPPING) {
            getPrintWriterManager().startMapping(sourceStartLine);
        }
        super.print(code);
        if (sourceStartLine > 0 || sourceStartLine == JavaCodeGenerator.CONTINUE_LAST_MAPPING) {
            getPrintWriterManager().endMapping();
        }
    }

    @Override
    protected void _print(String code) {
        _print(code, defaultLine);
    }
    protected void _print(String code, int sourceStartLine) {
        if (sourceStartLine > 0 || sourceStartLine == JavaCodeGenerator.CONTINUE_LAST_MAPPING) {
            getPrintWriterManager().startMapping(sourceStartLine);
        }
        super._print(code);
        if (sourceStartLine > 0 || sourceStartLine == JavaCodeGenerator.CONTINUE_LAST_MAPPING) {
            getPrintWriterManager().endMapping();
        }
    }

    @Override
    protected void _println(String code) {
        _println(code, defaultLine);
    }
    protected void _println(String code, int sourceStartLine) {
        if (sourceStartLine > 0 || sourceStartLine == JavaCodeGenerator.CONTINUE_LAST_MAPPING) {
            getPrintWriterManager().startMapping(sourceStartLine);
        }
        super._println(code);
        if (sourceStartLine > 0 || sourceStartLine == JavaCodeGenerator.CONTINUE_LAST_MAPPING) {
            getPrintWriterManager().endMapping();
        }
    }

    /** Adds a semantic predicate string to the sem pred vector
     These strings will be used to build an array of sem pred names
     when building a debugging parser.  This method should only be
     called when the debug option is specified
     * @param predicate The predicate string
     * @return the predicate number
     */
    protected int addSemPred(String predicate) {
        semPreds.add(predicate);
        return semPreds.size() - 1;
    }

    /**
     * Exit the tool if an error occurred
     */
    public void exitIfError() {
        if (antxrTool.hasError()) {
            antxrTool.fatalError("Exiting due to errors.");
        }
    }

    /**Generate the parser, lexer, treeparser, and token types in Java */
    @Override
    public void gen() {
        // Do the code generation
        try {
            // Loop over all grammars
            for (Grammar g : behavior.grammars.values()) {
                // Connect all the components to each other
                g.setGrammarAnalyzer(analyzer);
                g.setCodeGenerator(this);
                analyzer.setGrammar(g);
                // To get right overloading behavior across hetrogeneous grammars
                setupGrammarParameters(g);
                g.generate();
                // print out the grammar with lookahead sets (and FOLLOWs)
                // System.out.print(g.toString());
                exitIfError();
            }

            // Loop over all token managers (some of which are lexers)
            for (TokenManager tm : behavior.tokenManagers.values()) {
                if (!tm.isReadOnly()) {
                    // Write the token manager tokens as Java
                    // this must appear before genTokenInterchange so that
                    // labels are set on string literals
                    genTokenTypes(tm);
                    // Write the token manager tokens as plain text
                    genTokenInterchange(tm);
                }
                exitIfError();
            }
        }
        catch (IOException e) {
            antxrTool.reportException(e, null);
        }
    }

    /** Generate code for the given grammar element.
     * @param action The {...} action to generate
     */
    @Override
    public void gen(ActionElement action) {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = action.getLine();
        if (DEBUG_CODE_GENERATOR) {
            System.out.println("genAction(" + action + ")");
        }
        if (action.isSemPred) {
            genSemPred(action.actionText, action.line);
        }
        else {
            if (grammar.hasSyntacticPredicate) {
                    println("if ( inputState.guessing==0 ) {");
                tabs++;
            }

            // get the name of the followSet for the current rule so that we
            // can replace $FOLLOW in the .g file.
            ActionTransInfo tInfo = new ActionTransInfo();
            String actionStr = processActionForSpecialSymbols(action.actionText,
                                                              action.getLine(),
                                                              currentRule,
                                                              tInfo);

            if (tInfo.refRuleRoot != null) {
                // Somebody referenced "#rule", make sure translated var is valid
                // assignment to #rule is left as a ref also, meaning that assignments
                // with no other refs like "#rule = foo();" still forces this code to be
                // generated (unnecessarily).
                    println(tInfo.refRuleRoot + " = (" + labeledElementASTType + ")currentAST.root;");
            }

            // dump the translated action
                printAction(actionStr);

            if (tInfo.assignToRoot) {
                // Somebody did a "#rule=", reset internal currentAST.root
                    println("currentAST.root = " + tInfo.refRuleRoot + ";");
                // reset the child pointer too to be last sibling in sibling list
                println("currentAST.child = " + tInfo.refRuleRoot + "!=null &&" + tInfo.refRuleRoot + ".getFirstChild()!=null ?", JavaCodeGenerator.NO_MAPPING);
                tabs++;
                    println(tInfo.refRuleRoot + ".getFirstChild() : " + tInfo.refRuleRoot + ";");
                tabs--;
                    println("currentAST.advanceChildToEnd();");
            }

            if (grammar.hasSyntacticPredicate) {
                tabs--;
                println("}", JavaCodeGenerator.NO_MAPPING);
            }
        }
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** Generate code for the given grammar element.
     * @param blk The "x|y|z|..." block to generate
     */
    @Override
    public void gen(AlternativeBlock blk) {
        if (DEBUG_CODE_GENERATOR) {
            System.out.println("gen(" + blk + ")");
        }
        println("{", JavaCodeGenerator.NO_MAPPING);
        genBlockPreamble(blk);
        genBlockInitAction(blk);

        // Tell AST generation to build subrule result
        String saveCurrentASTResult = currentASTResult;
        if (blk.getLabel() != null) {
            currentASTResult = blk.getLabel();
        }

        grammar.theLLkAnalyzer.deterministic(blk);

        JavaBlockFinishingInfo howToFinish = genCommonBlock(blk, true);
        genBlockFinish(howToFinish, throwNoViable, blk.getLine());

        println("}", JavaCodeGenerator.NO_MAPPING);

        // Restore previous AST generation
        currentASTResult = saveCurrentASTResult;
    }

    /** Generate code for the given grammar element.
     * @param end The block-end element to generate.  Block-end
     * elements are synthesized by the grammar parser to represent
     * the end of a block.
     */
    @Override
    public void gen(BlockEndElement end) {
        if (DEBUG_CODE_GENERATOR) {
            System.out.println("genRuleEnd(" + end + ")");
        }
    }

    /** Generate code for the given grammar element.
     * @param atom The character literal reference to generate
     */
    @Override
    public void gen(CharLiteralElement atom) {
        if (DEBUG_CODE_GENERATOR) {
            System.out.println("genChar(" + atom + ")");
        }

        if (atom.getLabel() != null) {
            println(atom.getLabel() + " = " + lt1Value + ";", atom.getLine());
        }

        boolean oldsaveText = saveText;
        saveText = saveText && atom.getAutoGenType() == GrammarElement.AUTO_GEN_NONE;
        genMatch(atom);
        saveText = oldsaveText;
    }

    /** Generate code for the given grammar element.
     * @param r The character-range reference to generate
     */
    @Override
    public void gen(CharRangeElement r) {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = r.getLine();
        if (r.getLabel() != null && syntacticPredLevel == 0) {
                println(r.getLabel() + " = " + lt1Value + ";");
        }
        boolean flag = ( grammar instanceof LexerGrammar &&
            ( !saveText ||
            r.getAutoGenType() ==
            GrammarElement.AUTO_GEN_BANG ) );
        if (flag) {
                println("_saveIndex=text.length();");
        }

            println("matchRange(" + r.beginText + "," + r.endText + ");");

        if (flag) {
                println("text.setLength(_saveIndex);");
            }
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** Generate the lexer Java file
     * @param g The grammar to generate
     * @throws IOException for I/O errors
     */
    @Override
    public void gen(LexerGrammar g) throws IOException {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = JavaCodeGenerator.NO_MAPPING;
        // If debugging, create a new sempred vector for this grammar
        if (g.debuggingOutput) {
            semPreds = new ArrayList<String>();
        }

        setGrammar(g);
        if (!(grammar instanceof LexerGrammar)) {
            antxrTool.fatalError("panic: Internal error generating lexer");
        }

            // SAS: moved output creation to method so a subclass can change
            //      how the output is generated (for VAJ interface)
            currentOutput = getPrintWriterManager().setupOutput(antxrTool, grammar);

        genAST = false;	// no way to gen trees.
        saveText = true;	// save consumed characters.

        tabs = 0;

        // Generate header common to all Java output files
        genHeader();
        // Do not use printAction because we assume tabs==0

            try {
                defaultLine = behavior.getHeaderActionLine("");
                println(behavior.getHeaderAction(""));
            } finally {
                defaultLine = JavaCodeGenerator.NO_MAPPING;
            }

        // Generate header specific to lexer Java file
        // println("import java.io.FileInputStream;");
        println("import java.io.InputStream;");
        println("import com.javadude.antxr.TokenStreamException;");
        println("import com.javadude.antxr.TokenStreamIOException;");
        println("import com.javadude.antxr.TokenStreamRecognitionException;");
        println("import com.javadude.antxr.CharStreamException;");
        println("import com.javadude.antxr.CharStreamIOException;");
        println("import com.javadude.antxr.ANTXRException;");
        println("import java.io.Reader;");
        println("import java.util.HashMap;");
        println("import java.util.Map;");
        println("import java.util.List;");
        println("import java.util.ArrayList;");
        println("import com.javadude.antxr." + grammar.getSuperClass() + ";");
        println("import com.javadude.antxr.InputBuffer;");
        println("import com.javadude.antxr.ByteBuffer;");
        println("import com.javadude.antxr.CharBuffer;");
        println("import com.javadude.antxr.Token;");
        println("import com.javadude.antxr.CommonToken;");
        println("import com.javadude.antxr.RecognitionException;");
        println("import com.javadude.antxr.NoViableAltForCharException;");
        println("import com.javadude.antxr.MismatchedCharException;");
        println("import com.javadude.antxr.TokenStream;");
        println("import com.javadude.antxr.ANTXRHashString;");
        println("import com.javadude.antxr.LexerSharedInputState;");
        println("import com.javadude.antxr.collections.impl.BitSet;");
        println("import com.javadude.antxr.SemanticException;");

        // Generate user-defined lexer file preamble
            println(grammar.preambleAction.getText());

        // Generate lexer class definition
        String sup = null;
        if (grammar.superClass != null) {
            sup = grammar.superClass;
        }
        else {
            sup = "com.javadude.antxr." + grammar.getSuperClass();
        }

        // print javadoc comment if any
        if (grammar.comment != null) {
                _println(grammar.comment);
        }

        println("@SuppressWarnings(\"all\")");
        // get prefix (replaces "public" and lets user specify)
        String prefix = "public";
        Token tprefix = grammar.options.get("classHeaderPrefix");
        if (tprefix != null) {
            String p = StringUtils.stripFrontBack(tprefix.getText(), "\"", "\"");
            if (p != null) {
                prefix = p;
            }
        }

            print(prefix+" ");
            print("class " + grammar.getClassName() + " extends " + sup);
            println(" implements " + grammar.tokenManager.getName() + CodeGenerator.TokenTypesFileSuffix + ", TokenStream");
        Token tsuffix = grammar.options.get("classHeaderSuffix");
        if (tsuffix != null) {
            String suffix = StringUtils.stripFrontBack(tsuffix.getText(), "\"", "\"");
            if (suffix != null) {
                    print(", " + suffix);	// must be an interface name for Java
            }
        }
            println(" {");

        // Generate user-defined lexer class members
        print(
            processActionForSpecialSymbols(grammar.classMemberAction.getText(), grammar.classMemberAction.getLine(), currentRule, null),
            grammar.classMemberAction.getLine()
        );

        //
        // Generate the constructor from InputStream, which in turn
        // calls the ByteBuffer constructor
        //
            println("public " + grammar.getClassName() + "(InputStream in) {");
        tabs++;
            println("this(new ByteBuffer(in));");
        tabs--;
            println("}");

        //
        // Generate the constructor from Reader, which in turn
        // calls the CharBuffer constructor
        //
            println("public " + grammar.getClassName() + "(Reader in) {");
        tabs++;
            println("this(new CharBuffer(in));");
        tabs--;
            println("}");

            println("public " + grammar.getClassName() + "(InputBuffer ib) {");
        tabs++;
        // if debugging, wrap the input buffer in a debugger
        if (grammar.debuggingOutput) {
            println("this(new LexerSharedInputState(new com.javadude.antxr.debug.DebuggingInputBuffer(ib)));");
        } else {
            println("this(new LexerSharedInputState(ib));");
        }
        tabs--;
            println("}");

        //
        // Generate the constructor from InputBuffer (char or byte)
        //
            println("public " + grammar.getClassName() + "(LexerSharedInputState state) {");
        tabs++;

            println("super(state);");
        // if debugging, set up array variables and call user-overridable
        //   debugging setup method
        if (grammar.debuggingOutput) {
                println("  ruleNames  = _ruleNames;");
                println("  semPredNames = _semPredNames;");
                println("  setupDebugging();");
        }

        // Generate the setting of various generated options.
        // These need to be before the literals since ANTXRHashString depends on
        // the casesensitive stuff.
            println("caseSensitiveLiterals = " + g.caseSensitiveLiterals + ";");
            println("setCaseSensitive(" + g.caseSensitive + ");");

        // Generate the initialization of a hashtable
        // containing the string literals used in the lexer
        // The literals variable itself is in CharScanner
            println("literals = new HashMap<ANTXRHashString, Integer>();");
        Iterator<String> keys = grammar.tokenManager.getTokenSymbolKeys();
        while (keys.hasNext()) {
            String key = keys.next();
            if (key.charAt(0) != '"') {
                continue;
            }
            TokenSymbol sym = grammar.tokenManager.getTokenSymbol(key);
            if (sym instanceof StringLiteralSymbol) {
                StringLiteralSymbol s = (StringLiteralSymbol)sym;
                println("literals.put(new ANTXRHashString(" + s.getId() + ", this), new Integer(" + s.getTokenType() + "));");
            }
        }
        tabs--;

        println("}");

        // generate the rule name array for debugging
        if (grammar.debuggingOutput) {
                println("private static final String _ruleNames[] = {");

            for (GrammarSymbol sym : grammar.rules) {
                if (sym instanceof RuleSymbol) {
                    println("  \"" + ((RuleSymbol)sym).getId() + "\",");
                }
            }
                println("};");
        }

        // Generate nextToken() rule.
        // nextToken() is a synthetic lexer rule that is the implicit OR of all
        // user-defined lexer rules.
        genNextToken();

        // Generate code for each rule in the lexer
        int ruleNum = 0;
        for (RuleSymbol sym : grammar.rules) {
            // Don't generate the synthetic rules
            if (!sym.getId().equals("mnextToken")) {
                genRule(sym, false, ruleNum++);
            }
            exitIfError();
        }

        // Generate the semantic predicate map for debugging
        if (grammar.debuggingOutput) {
            genSemPredMap();
        }

        // Generate the bitsets used throughout the lexer
        genBitsets(bitsetsUsed, ((LexerGrammar)grammar).charVocabulary.size());

            println("");
            println("}");

        // Close the lexer output stream
        getPrintWriterManager().finishOutput();
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** Generate code for the given grammar element.
     * @param blk The (...)+ block to generate
     */
    @Override
    public void gen(OneOrMoreBlock blk) {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = blk.getLine();
        if (DEBUG_CODE_GENERATOR) {
            System.out.println("gen+(" + blk + ")");
        }
        String label;
        String cnt;
        println("{", JavaCodeGenerator.NO_MAPPING);
        genBlockPreamble(blk);
        if (blk.getLabel() != null) {
            cnt = "_cnt_" + blk.getLabel();
        }
        else {
            cnt = "_cnt" + blk.ID;
        }
            println("int " + cnt + "=0;");
        if (blk.getLabel() != null) {
            label = blk.getLabel();
        }
        else {
            label = "_loop" + blk.ID;
        }
            println(label + ":");
            println("do {");
        tabs++;
        // generate the init action for ()+ ()* inside the loop
        // this allows us to do usefull EOF checking...
        genBlockInitAction(blk);

        // Tell AST generation to build subrule result
        String saveCurrentASTResult = currentASTResult;
        if (blk.getLabel() != null) {
            currentASTResult = blk.getLabel();
        }

        grammar.theLLkAnalyzer.deterministic(blk);

        // generate exit test if greedy set to false
        // and an alt is ambiguous with exit branch
        // or when lookahead derived purely from end-of-file
        // Lookahead analysis stops when end-of-file is hit,
        // returning set {epsilon}.  Since {epsilon} is not
        // ambig with any real tokens, no error is reported
        // by deterministic() routines and we have to check
        // for the case where the lookahead depth didn't get
        // set to NONDETERMINISTIC (this only happens when the
        // FOLLOW contains real atoms + epsilon).
        boolean generateNonGreedyExitPath = false;
        int nonGreedyExitDepth = grammar.maxk;

        if (!blk.greedy &&
            blk.exitLookaheadDepth <= grammar.maxk &&
            blk.exitCache[blk.exitLookaheadDepth].containsEpsilon()) {
            generateNonGreedyExitPath = true;
            nonGreedyExitDepth = blk.exitLookaheadDepth;
        }
        else if (!blk.greedy &&
            blk.exitLookaheadDepth == LLkGrammarAnalyzer.NONDETERMINISTIC) {
            generateNonGreedyExitPath = true;
        }

        // generate exit test if greedy set to false
        // and an alt is ambiguous with exit branch
        if (generateNonGreedyExitPath) {
            if (DEBUG_CODE_GENERATOR) {
                System.out.println("nongreedy (...)+ loop; exit depth is " +
                                   blk.exitLookaheadDepth);
            }
            String predictExit =
                getLookaheadTestExpression(blk.exitCache,
                                           nonGreedyExitDepth);
            println("// nongreedy exit test", JavaCodeGenerator.NO_MAPPING);
            println("if ( " + cnt + ">=1 && " + predictExit + ") break " + label + ";", JavaCodeGenerator.CONTINUE_LAST_MAPPING);
        }

        JavaBlockFinishingInfo howToFinish = genCommonBlock(blk, false);
        genBlockFinish(
            howToFinish,
            "if ( " + cnt + ">=1 ) { break " + label + "; } else {" + throwNoViable + "}",
            blk.getLine()
        );

            println(cnt + "++;");
        tabs--;
            println("} while (true);");
            println("}");

        // Restore previous AST generation
        currentASTResult = saveCurrentASTResult;
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** {@inheritDoc} */
    @Override
    public void gen(ParserGrammar g) throws IOException {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = JavaCodeGenerator.NO_MAPPING;
        // if debugging, set up a new vector to keep track of sempred
        //   strings for this grammar
        if (g.debuggingOutput) {
            semPreds = new ArrayList<String>();
        }

        setGrammar(g);
        if (!(grammar instanceof ParserGrammar)) {
            antxrTool.fatalError("panic: Internal error generating parser");
        }

            // Open the output stream for the parser and set the currentOutput
            // SAS: moved file setup so subclass could do it (for VAJ interface)
            currentOutput = getPrintWriterManager().setupOutput(antxrTool,grammar);

        genAST = grammar.buildAST;

        tabs = 0;

        // Generate the header common to all output files.
        genHeader();
        // Do not use printAction because we assume tabs==0
            try {
                defaultLine = behavior.getHeaderActionLine("");
                println(behavior.getHeaderAction(""));
            } finally {
                defaultLine = JavaCodeGenerator.NO_MAPPING;
            }

        // Generate header for the parser
        println("import com.javadude.antxr.TokenBuffer;");
        println("import com.javadude.antxr.TokenStreamException;");
        println("import com.javadude.antxr.TokenStreamIOException;");
        println("import com.javadude.antxr.ANTXRException;");
        println("import com.javadude.antxr." + grammar.getSuperClass() + ";");
        println("import com.javadude.antxr.Token;");
        println("import com.javadude.antxr.TokenStream;");
        println("import com.javadude.antxr.RecognitionException;");
        println("import com.javadude.antxr.NoViableAltException;");
        println("import com.javadude.antxr.MismatchedTokenException;");
        println("import com.javadude.antxr.SemanticException;");
        println("import com.javadude.antxr.ParserSharedInputState;");
        println("import com.javadude.antxr.collections.impl.BitSet;");
        if ( genAST ) {
            println("");
            println("// AST generation support");
            println("import com.javadude.antxr.collections.AST;");
            println("import com.javadude.antxr.ASTFactory;");
            println("import com.javadude.antxr.ASTPair;");
            println("import com.javadude.antxr.collections.impl.ASTArray;");
        }

        println("");
        println("// ANTXR XML Mode Support");
        println("import com.javadude.antxr.scanner.XMLToken;");
        println("import com.javadude.antxr.scanner.Attribute;");
        println("import java.util.Map;");
        println("import java.util.HashMap;");
        println("");

        // Output the user-defined parser preamble
        println(grammar.preambleAction.getText());

        // Generate parser class definition
        String sup = null;
        if (grammar.superClass != null) {
            sup = grammar.superClass;
        } else {
            sup = "com.javadude.antxr." + grammar.getSuperClass();
        }

        // print javadoc comment if any
        if (grammar.comment != null) {
                _println(grammar.comment);
        }
        println("@SuppressWarnings(\"all\")");

        // get prefix (replaces "public" and lets user specify)
        String prefix = "public";
        Token tprefix = grammar.options.get("classHeaderPrefix");
        if (tprefix != null) {
            String p = StringUtils.stripFrontBack(tprefix.getText(), "\"", "\"");
            if (p != null) {
                prefix = p;
            }
        }

            print(prefix+" ");
            print("class " + grammar.getClassName() + " extends " + sup);
            println("       implements " + grammar.tokenManager.getName() + CodeGenerator.TokenTypesFileSuffix);

        Token tsuffix = grammar.options.get("classHeaderSuffix");
        if (tsuffix != null) {
            String suffix = StringUtils.stripFrontBack(tsuffix.getText(), "\"", "\"");
            if (suffix != null) {
                print(", " + suffix);	// must be an interface name for Java
            }
        }
            println(" {");

        // set up an array of all the rule names so the debugger can
        // keep track of them only by number -- less to store in tree...
        if (grammar.debuggingOutput) {
                println("private static final String _ruleNames[] = {");

            for (GrammarSymbol sym : grammar.rules) {
                if (sym instanceof RuleSymbol) {
                    println("  \"" + ((RuleSymbol)sym).getId() + "\",");
                }
            }
                println("};");
        }

        println("	// ANTXR XML Mode Support");
        println("	private static Map<String, String> __xml_namespaceMap = new HashMap<String, String>();");
        println("	public static Map<String, String> getNamespaceMap() {return __xml_namespaceMap;}");
        println("	public static String resolveNamespace(String prefix) {");
        println("		if (prefix == null || \"\".equals(prefix))");
        println("			return \"\";");
        println("		return __xml_namespaceMap.get(prefix);");
        println("	}");
        if (grammar.namespaceMap != null && !grammar.namespaceMap.isEmpty()) {
            println("	static {");
            for (Iterator<String> i = grammar.namespaceMap.keySet().iterator(); i.hasNext();) {
                String namespacePrefix = i.next();
                String namespace = grammar.namespaceMap.get(namespacePrefix);
                println("		__xml_namespaceMap.put(\""+namespacePrefix+"\",\""+namespace+"\");");
            }
            println(" }");
        }
        println("");

        // Generate user-defined parser class members
        print(
            processActionForSpecialSymbols(grammar.classMemberAction.getText(), grammar.classMemberAction.getLine(), currentRule, null),
            grammar.classMemberAction.getLine()
        );

        // Generate parser class constructor from TokenBuffer
            println("");
            println("protected " + grammar.getClassName() + "(TokenBuffer tokenBuf, int k) {");
            println("  super(tokenBuf,k);");
            println("  tokenNames = _tokenNames;");
        // if debugging, set up arrays and call the user-overridable
        //   debugging setup method
        if (grammar.debuggingOutput) {
                println("  ruleNames  = _ruleNames;");
                println("  semPredNames = _semPredNames;");
                println("  setupDebugging(tokenBuf);");
        }
        if ( grammar.buildAST ) {
                println("  buildTokenTypeASTClassMap();");
                println("  astFactory = new ASTFactory(getTokenTypeToASTClassMap());");
        }
            println("}");
            println("");

            println("public " + grammar.getClassName() + "(TokenBuffer tokenBuf) {");
            println("  this(tokenBuf," + grammar.maxk + ");");
            println("}");
            println("");

        // Generate parser class constructor from TokenStream
            println("protected " + grammar.getClassName() + "(TokenStream lexer, int k) {");
            println("  super(lexer,k);");
            println("  tokenNames = _tokenNames;");

        // if debugging, set up arrays and call the user-overridable
        //   debugging setup method
        if (grammar.debuggingOutput) {
                println("  ruleNames  = _ruleNames;");
                println("  semPredNames = _semPredNames;");
                println("  setupDebugging(lexer);");
        }
        if ( grammar.buildAST ) {
                println("  buildTokenTypeASTClassMap();");
                println("  astFactory = new ASTFactory(getTokenTypeToASTClassMap());");
        }
            println("}");
            println("");

            println("public " + grammar.getClassName() + "(TokenStream lexer) {");
            println("  this(lexer," + grammar.maxk + ");");
            println("}");
            println("");

            println("public " + grammar.getClassName() + "(ParserSharedInputState state) {");
            println("  super(state," + grammar.maxk + ");");
            println("  tokenNames = _tokenNames;");
        if ( grammar.buildAST ) {
                println("  buildTokenTypeASTClassMap();");
                println("  astFactory = new ASTFactory(getTokenTypeToASTClassMap());");
        }
            println("}");
            println("");

        // Generate code for each rule in the grammar
        int ruleNum = 0;
        for (GrammarSymbol sym : grammar.rules) {
            if (sym instanceof RuleSymbol) {
                RuleSymbol rs = (RuleSymbol)sym;
                genRule(rs, rs.references.size() == 0, ruleNum++);
            }
            exitIfError();
        }

        // Generate the token names
        genTokenStrings();

        if ( grammar.buildAST ) {
            genTokenASTNodeMap();
        }

        // Generate the bitsets used throughout the grammar
        genBitsets(bitsetsUsed, grammar.tokenManager.maxTokenType());

        // Generate the semantic predicate map for debugging
        if (grammar.debuggingOutput) {
            genSemPredMap();
        }

        // Close class definition
            println("");
            println("}");

        // Close the parser output stream
        getPrintWriterManager().finishOutput();
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** Generate code for the given grammar element.
     * @param rr The rule-reference to generate
     */
    @Override
    public void gen(RuleRefElement rr) {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = rr.getLine();
        if (DEBUG_CODE_GENERATOR) {
            System.out.println("genRR(" + rr + ")");
        }
        RuleSymbol rs = (RuleSymbol)grammar.getSymbol(rr.targetRule);
        if (rs == null || !rs.isDefined()) {
            // Is this redundant???
            antxrTool.error("Rule '" + rr.targetRule + "' is not defined", grammar.getFilename(), rr.getLine(), rr.getColumn());
            return;
        }

        genErrorTryForElement(rr);

        // AST value for labeled rule refs in tree walker.
        // This is not AST construction;  it is just the input tree node value.
        if (grammar instanceof TreeWalkerGrammar &&
            rr.getLabel() != null &&
            syntacticPredLevel == 0) {
                println(rr.getLabel() + " = _t==ASTNULL ? null : " + lt1Value + ";");
        }

        // if in lexer and ! on rule ref or alt or rule, save buffer index to kill later
        if (grammar instanceof LexerGrammar && (!saveText || rr.getAutoGenType() == GrammarElement.AUTO_GEN_BANG)) {
                println("_saveIndex=text.length();");
        }

        // Process return value assignment if any
        printTabs();
        if (rr.idAssign != null) {
            // Warn if the rule has no return type
            if (rs.block.returnAction == null) {
                antxrTool.warning("Rule '" + rr.targetRule + "' has no return type", grammar.getFilename(), rr.getLine(), rr.getColumn());
            }
                _print(rr.idAssign + "=");
        }
        else {
            // Warn about return value if any, but not inside syntactic predicate
            if (!(grammar instanceof LexerGrammar) && syntacticPredLevel == 0 && rs.block.returnAction != null) {
                antxrTool.warning("Rule '" + rr.targetRule + "' returns a value", grammar.getFilename(), rr.getLine(), rr.getColumn());
            }
        }

        // Call the rule
        GenRuleInvocation(rr);

        // if in lexer and ! on element or alt or rule, save buffer index to kill later
        if (grammar instanceof LexerGrammar && (!saveText || rr.getAutoGenType() == GrammarElement.AUTO_GEN_BANG)) {
                println("text.setLength(_saveIndex);");
        }

        // if not in a syntactic predicate
        if (syntacticPredLevel == 0) {
            boolean doNoGuessTest = (
                grammar.hasSyntacticPredicate &&
                (
                grammar.buildAST && rr.getLabel() != null ||
                (genAST && rr.getAutoGenType() == GrammarElement.AUTO_GEN_NONE)
                )
                );
            if (doNoGuessTest) {
                // println("if (inputState.guessing==0) {");
                // tabs++;
            }

            if (grammar.buildAST && rr.getLabel() != null) {
                // always gen variable for rule return on labeled rules
                    println(rr.getLabel() + "_AST = (" + labeledElementASTType + ")returnAST;");
            }
            if (genAST) {
                switch (rr.getAutoGenType()) {
                    case GrammarElement.AUTO_GEN_NONE:
                        // println("theASTFactory.addASTChild(currentAST, returnAST);");
                            println("astFactory.addASTChild(currentAST, returnAST);");
                        break;
                    case GrammarElement.AUTO_GEN_CARET:
                        antxrTool.error("Internal: encountered ^ after rule reference");
                        break;
                    default:
                        break;
                }
            }

            // if a lexer and labeled, Token label defined at rule level, just set it here
            if (grammar instanceof LexerGrammar && rr.getLabel() != null) {
                    println(rr.getLabel() + "=_returnToken;");
            }

            if (doNoGuessTest) {
                // tabs--;
                // println("}");
            }
        }
        genErrorCatchForElement(rr);
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** Generate code for the given grammar element.
     * @param atom The string-literal reference to generate
     */
    @Override
    public void gen(StringLiteralElement atom) {
        if (DEBUG_CODE_GENERATOR) {
            System.out.println("genString(" + atom + ")");
        }

        // Variable declarations for labeled elements
        if (atom.getLabel() != null && syntacticPredLevel == 0) {
            println(atom.getLabel() + " = " + lt1Value + ";", atom.getLine());
        }

        // AST
        genElementAST(atom);

        // is there a bang on the literal?
        boolean oldsaveText = saveText;
        saveText = saveText && atom.getAutoGenType() == GrammarElement.AUTO_GEN_NONE;

        // matching
        genMatch(atom);

        saveText = oldsaveText;

        // tack on tree cursor motion if doing a tree walker
        if (grammar instanceof TreeWalkerGrammar) {
            println("_t = _t.getNextSibling();", atom.getLine());
        }
    }

    /** Generate code for the given grammar element.
     * @param r The token-range reference to generate
     */
    @Override
    public void gen(TokenRangeElement r) {
        genErrorTryForElement(r);
        if (r.getLabel() != null && syntacticPredLevel == 0) {
            println(r.getLabel() + " = " + lt1Value + ";", r.getLine());
        }

        // AST
        genElementAST(r);

        // match
        println("matchRange(" + r.beginText + "," + r.endText + ");", r.getLine());
        genErrorCatchForElement(r);
    }

    /** Generate code for the given grammar element.
     * @param atom The token-reference to generate
     */
    @Override
    public void gen(TokenRefElement atom) {
        if (DEBUG_CODE_GENERATOR) {
            System.out.println("genTokenRef(" + atom + ")");
        }
        if (grammar instanceof LexerGrammar) {
            antxrTool.fatalError("panic: Token reference found in lexer");
        }
        genErrorTryForElement(atom);
        // Assign Token value to token label variable
        if (atom.getLabel() != null && syntacticPredLevel == 0) {
            println(atom.getLabel() + " = " + lt1Value + ";", atom.getLine());
        }

        // AST
        genElementAST(atom);
        // matching
        genMatch(atom);
        genErrorCatchForElement(atom);

        // tack on tree cursor motion if doing a tree walker
        if (grammar instanceof TreeWalkerGrammar) {
            println("_t = _t.getNextSibling();", atom.getLine());
        }
    }

    /** {@inheritDoc} */
    @Override
    public void gen(TreeElement t) {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = t.getLine();
        // save AST cursor
            println("AST __t" + t.ID + " = _t;");

        // If there is a label on the root, then assign that to the variable
        if (t.root.getLabel() != null) {
            println(t.root.getLabel() + " = _t==ASTNULL ? null :(" + labeledElementASTType + ")_t;", t.root.getLine());
        }

        // check for invalid modifiers ! and ^ on tree element roots
        if ( t.root.getAutoGenType() == GrammarElement.AUTO_GEN_BANG ) {
            antxrTool.error("Suffixing a root node with '!' is not implemented",
                         grammar.getFilename(), t.getLine(), t.getColumn());
            t.root.setAutoGenType(GrammarElement.AUTO_GEN_NONE);
        }
        if ( t.root.getAutoGenType() == GrammarElement.AUTO_GEN_CARET ) {
            antxrTool.warning("Suffixing a root node with '^' is redundant; already a root",
                         grammar.getFilename(), t.getLine(), t.getColumn());
            t.root.setAutoGenType(GrammarElement.AUTO_GEN_NONE);
        }

        // Generate AST variables
        genElementAST(t.root);
        if (grammar.buildAST) {
            // Save the AST construction state
                println("ASTPair __currentAST" + t.ID + " = currentAST.copy();");
            // Make the next item added a child of the TreeElement root
                println("currentAST.root = currentAST.child;");
                println("currentAST.child = null;");
        }

        // match root
        if ( t.root instanceof WildcardElement ) {
            println("if ( _t==null ) throw new MismatchedTokenException();", t.root.getLine());
        }
        else {
            genMatch(t.root);
        }
        // move to list of children
            println("_t = _t.getFirstChild();");

        // walk list of children, generating code for each
        for (int i = 0; i < t.getAlternatives().size(); i++) {
            Alternative a = t.getAlternativeAt(i);
            AlternativeElement e = a.head;
            while (e != null) {
                e.generate();
                e = e.next;
            }
        }

        if (grammar.buildAST) {
            // restore the AST construction state to that just after the
            // tree root was added
                println("currentAST = __currentAST" + t.ID + ";");
        }
        // restore AST cursor
            println("_t = __t" + t.ID + ";");
        // move cursor to sibling of tree just parsed
            println("_t = _t.getNextSibling();");
         } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** {@inheritDoc} */
    @Override
    public void gen(TreeWalkerGrammar g) throws IOException {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = JavaCodeGenerator.NO_MAPPING;
            // SAS: debugging stuff removed for now...
        setGrammar(g);
        if (!(grammar instanceof TreeWalkerGrammar)) {
            antxrTool.fatalError("panic: Internal error generating tree-walker");
        }
            // Open the output stream for the parser and set the currentOutput
            // SAS: move file open to method so subclass can override it
            //      (mainly for VAJ interface)
            currentOutput = getPrintWriterManager().setupOutput(antxrTool,grammar);

        genAST = grammar.buildAST;
        tabs = 0;

        // Generate the header common to all output files.
        genHeader();
        // Do not use printAction because we assume tabs==0
            try {
                defaultLine = behavior.getHeaderActionLine("");
                println(behavior.getHeaderAction(""));
            } finally {
                defaultLine = JavaCodeGenerator.NO_MAPPING;
            }

        // Generate header for the parser
        println("import com.javadude.antxr." + grammar.getSuperClass() + ";");
        println("import com.javadude.antxr.Token;");
        println("import com.javadude.antxr.collections.AST;");
        println("import com.javadude.antxr.RecognitionException;");
        println("import com.javadude.antxr.ANTXRException;");
        println("import com.javadude.antxr.NoViableAltException;");
        println("import com.javadude.antxr.MismatchedTokenException;");
        println("import com.javadude.antxr.SemanticException;");
        println("import com.javadude.antxr.collections.impl.BitSet;");
        println("import com.javadude.antxr.ASTPair;");
        println("import com.javadude.antxr.collections.impl.ASTArray;");

        // Output the user-defined parser premamble
            println(grammar.preambleAction.getText());

        // Generate parser class definition
        String sup = null;
        if (grammar.superClass != null) {
            sup = grammar.superClass;
        }
        else {
            sup = "com.javadude.antxr." + grammar.getSuperClass();
        }
            println("");

        // print javadoc comment if any
        if (grammar.comment != null) {
                _println(grammar.comment);
        }

        println("@SuppressWarnings(\"all\")");
        // get prefix (replaces "public" and lets user specify)
        String prefix = "public";
        Token tprefix = grammar.options.get("classHeaderPrefix");
        if (tprefix != null) {
            String p = StringUtils.stripFrontBack(tprefix.getText(), "\"", "\"");
            if (p != null) {
                prefix = p;
            }
        }

            print(prefix+" ");
            print("class " + grammar.getClassName() + " extends " + sup);
            println("       implements " + grammar.tokenManager.getName() + CodeGenerator.TokenTypesFileSuffix);
        Token tsuffix = grammar.options.get("classHeaderSuffix");
        if (tsuffix != null) {
            String suffix = StringUtils.stripFrontBack(tsuffix.getText(), "\"", "\"");
            if (suffix != null) {
                    print(", " + suffix);	// must be an interface name for Java
            }
        }
            println(" {");

        // Generate user-defined parser class members
        print(
            processActionForSpecialSymbols(grammar.classMemberAction.getText(), grammar.classMemberAction.getLine(), currentRule, null),
            grammar.classMemberAction.getLine()
        );

        // Generate default parser class constructor
            println("public " + grammar.getClassName() + "() {");
        tabs++;
            println("tokenNames = _tokenNames;");
        tabs--;
            println("}");
            println("");

        // Generate code for each rule in the grammar
        int ruleNum = 0;
        for (GrammarSymbol sym : grammar.rules) {
            if (sym instanceof RuleSymbol) {
                RuleSymbol rs = (RuleSymbol)sym;
                genRule(rs, rs.references.size() == 0, ruleNum++);
            }
            exitIfError();
        }

        // Generate the token names
        genTokenStrings();

        // Generate the bitsets used throughout the grammar
        genBitsets(bitsetsUsed, grammar.tokenManager.maxTokenType());

        // Close class definition
            println("}");
            println("");

        // Close the parser output stream
        getPrintWriterManager().finishOutput();
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** Generate code for the given grammar element.
     * @param wc The wildcard element to generate
     */
    @Override
    public void gen(WildcardElement wc) {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = wc.getLine();
        // Variable assignment for labeled elements
        if (wc.getLabel() != null && syntacticPredLevel == 0) {
                println(wc.getLabel() + " = " + lt1Value + ";");
        }

        // AST
        genElementAST(wc);
        // Match anything but EOF
        if (grammar instanceof TreeWalkerGrammar) {
                println("if ( _t==null ) throw new MismatchedTokenException();");
        }
        else if (grammar instanceof LexerGrammar) {
            if (grammar instanceof LexerGrammar &&
                (!saveText || wc.getAutoGenType() == GrammarElement.AUTO_GEN_BANG)) {
                    println("_saveIndex=text.length();");
            }
                println("matchNot(EOF_CHAR);");
            if (grammar instanceof LexerGrammar &&
                (!saveText || wc.getAutoGenType() == GrammarElement.AUTO_GEN_BANG)) {
                    println("text.setLength(_saveIndex);"); // kill text atom put in buffer
            }
        }
        else {
                println("matchNot(" + getValueString(Token.EOF_TYPE) + ");");
        }

        // tack on tree cursor motion if doing a tree walker
        if (grammar instanceof TreeWalkerGrammar) {
                println("_t = _t.getNextSibling();");
            }
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** Generate code for the given grammar element.
     * @param blk The (...)* block to generate@Override
     */
    @Override
    public void gen(ZeroOrMoreBlock blk) {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = blk.getLine();
        if (DEBUG_CODE_GENERATOR) {
            System.out.println("gen*(" + blk + ")");
        }
            println("{");
        genBlockPreamble(blk);
        String label;
        if (blk.getLabel() != null) {
            label = blk.getLabel();
        }
        else {
            label = "_loop" + blk.ID;
        }
            println(label + ":");
            println("do {");
        tabs++;
        // generate the init action for ()* inside the loop
        // this allows us to do usefull EOF checking...
        genBlockInitAction(blk);

        // Tell AST generation to build subrule result
        String saveCurrentASTResult = currentASTResult;
        if (blk.getLabel() != null) {
            currentASTResult = blk.getLabel();
        }

        grammar.theLLkAnalyzer.deterministic(blk);

        // generate exit test if greedy set to false
        // and an alt is ambiguous with exit branch
        // or when lookahead derived purely from end-of-file
        // Lookahead analysis stops when end-of-file is hit,
        // returning set {epsilon}.  Since {epsilon} is not
        // ambig with any real tokens, no error is reported
        // by deterministic() routines and we have to check
        // for the case where the lookahead depth didn't get
        // set to NONDETERMINISTIC (this only happens when the
        // FOLLOW contains real atoms + epsilon).
        boolean generateNonGreedyExitPath = false;
        int nonGreedyExitDepth = grammar.maxk;

        if (!blk.greedy &&
            blk.exitLookaheadDepth <= grammar.maxk &&
            blk.exitCache[blk.exitLookaheadDepth].containsEpsilon()) {
            generateNonGreedyExitPath = true;
            nonGreedyExitDepth = blk.exitLookaheadDepth;
        }
        else if (!blk.greedy &&
            blk.exitLookaheadDepth == LLkGrammarAnalyzer.NONDETERMINISTIC) {
            generateNonGreedyExitPath = true;
        }
        if (generateNonGreedyExitPath) {
            if (DEBUG_CODE_GENERATOR) {
                System.out.println("nongreedy (...)* loop; exit depth is " +
                                   blk.exitLookaheadDepth);
            }
            String predictExit =
                getLookaheadTestExpression(blk.exitCache,
                                           nonGreedyExitDepth);
                println("// nongreedy exit test");
                println("if (" + predictExit + ") break " + label + ";");
        }

        JavaBlockFinishingInfo howToFinish = genCommonBlock(blk, false);
        genBlockFinish(howToFinish, "break " + label + ";", blk.getLine());

        tabs--;
            println("} while (true);");
            println("}");

        // Restore previous AST generation
        currentASTResult = saveCurrentASTResult;
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** Generate an alternative.
     * @param alt  The alternative to generate
     * @param blk The block to which the alternative belongs
     */
    protected void genAlt(Alternative alt, AlternativeBlock blk) {
        // Save the AST generation state, and set it to that of the alt
        boolean savegenAST = genAST;
        genAST = genAST && alt.getAutoGen();

        boolean oldsaveTest = saveText;
        saveText = saveText && alt.getAutoGen();

        // Reset the variable name map for the alternative
        Map<String, String> saveMap = treeVariableMap;
        treeVariableMap = new HashMap<String, String>();

        // Generate try block around the alt for  error handling
        if (alt.exceptionSpec != null) {
            println("try {      // for error handling", alt.head.getLine());
            tabs++;
        }

        AlternativeElement elem = alt.head;
        while (!(elem instanceof BlockEndElement)) {
            elem.generate(); // alt can begin with anything. Ask target to gen.
            elem = elem.next;
        }

        if (genAST) {
            if (blk instanceof RuleBlock) {
                // Set the AST return value for the rule
                RuleBlock rblk = (RuleBlock)blk;
                if (grammar.hasSyntacticPredicate) {
                    // println("if ( inputState.guessing==0 ) {");
                    // tabs++;
                }
                println(rblk.getRuleName() + "_AST = (" + labeledElementASTType + ")currentAST.root;", JavaCodeGenerator.CONTINUE_LAST_MAPPING);
                if (grammar.hasSyntacticPredicate) {
                    // --tabs;
                    // println("}");
                }
            }
            else if (blk.getLabel() != null) {
                // ### future: also set AST value for labeled subrules.
                // println(blk.getLabel() + "_AST = ("+labeledElementASTType+")currentAST.root;");
                antxrTool.warning("Labeled subrules not yet supported", grammar.getFilename(), blk.getLine(), blk.getColumn());
            }
        }

        if (alt.exceptionSpec != null) {
            // close try block
            tabs--;
            println("}", JavaCodeGenerator.NO_MAPPING);
            genErrorHandler(alt.exceptionSpec);
        }

        genAST = savegenAST;
        saveText = oldsaveTest;

        treeVariableMap = saveMap;
    }

    /** Generate all the bitsets to be used in the parser or lexer
     * Generate the raw bitset data like "long _tokenSet1_data[] = {...};"
     * and the BitSet object declarations like "BitSet _tokenSet1 = new BitSet(_tokenSet1_data);"
     * Note that most languages do not support object initialization inside a
     * class definition, so other code-generators may have to separate the
     * bitset declarations from the initializations (e.g., put the initializations
     * in the generated constructor instead).
     * @param bitsetList The list of bitsets to generate.
     * @param maxVocabulary Ensure that each generated bitset can contain at least this value.
     */
    protected void genBitsets(List<BitSet> bitsetList,
                              int maxVocabulary
                              ) {
        println("", JavaCodeGenerator.NO_MAPPING);
        for (int i = 0; i < bitsetList.size(); i++) {
            BitSet p = bitsetList.get(i);
            // Ensure that generated BitSet is large enough for vocabulary
            p.growToInclude(maxVocabulary);
            genBitSet(p, i);
        }
    }

    /** Do something simple like:
     *  private static final long[] mk_tokenSet_0() {
     *    long[] data = { -2305839160922996736L, 63L, 16777216L, 0L, 0L, 0L };
     *    return data;
     *  }
     *  public static final BitSet _tokenSet_0 = new BitSet(mk_tokenSet_0());
     *
     *  Or, for large bitsets, optimize init so ranges are collapsed into loops.
     *  This is most useful for lexers using unicode.
     * @param p The bitset
     * @param id the id
     */
    private void genBitSet(BitSet p, int id) {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = JavaCodeGenerator.NO_MAPPING;
        // initialization data
        println(
                "private static final long[] mk" + getBitsetName(id) + "() {"
        );
        int n = p.lengthInLongWords();
        if ( n<CodeGenerator.BITSET_OPTIMIZE_INIT_THRESHOLD ) {
                println("\tlong[] data = { " + p.toStringOfWords() + "};");
        }
        else {
            // will init manually, allocate space then set values
                println("\tlong[] data = new long["+n+"];");
            long[] elems = p.toPackedArray();
            for (int i = 0; i < elems.length;) {
                if ( elems[i]==0 ) {
                    // done automatically by Java, don't waste time/code
                    i++;
                    continue;
                }
                if ( (i+1)==elems.length || elems[i]!=elems[i+1] ) {
                    // last number or no run of numbers, just dump assignment
                        println("\tdata["+i+"]="+elems[i]+"L;");
                    i++;
                }
                else {
                    // scan to find end of run
                    int j;
                    for (j = i + 1;
                         j < elems.length && elems[j]==elems[i];
                         j++)
                    {
                        // do nothing -- just scanning to find the end
                    }
                    // j-1 is last member of run
                    println("\tfor (int i = "+i+"; i<="+(j-1)+"; i++) { data[i]="+
                                elems[i]+"L; }");
                    i = j;
                }
            }
        }

            println("\treturn data;");
            println("}");
        // BitSet object
        println(
            "public static final BitSet " + getBitsetName(id) + " = new BitSet(" +
            "mk" + getBitsetName(id) + "()" +
                ");"
        );
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** Generate the finish of a block, using a combination of the info
     * returned from genCommonBlock() and the action to perform when
     * no alts were taken
     * @param howToFinish The return of genCommonBlock()
     * @param noViableAction What to generate when no alt is taken
     * @param line the line number of the block
     */
    private void genBlockFinish(JavaBlockFinishingInfo howToFinish, String noViableAction, int line) {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = line;
        if (howToFinish.needAnErrorClause &&
            (howToFinish.generatedAnIf || howToFinish.generatedSwitch)) {
            if (howToFinish.generatedAnIf) {
                    println("else {");
            }
            else {
                    println("{");
            }
            tabs++;
                println(noViableAction);
            tabs--;
                println("}");
        }

        if (howToFinish.postscript != null) {
                println(howToFinish.postscript);
            }
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** Generate the init action for a block, which may be a RuleBlock or a
     * plain AlternativeBLock.
     * @param blk The block for which the preamble is to be generated.
     */
    protected void genBlockInitAction(AlternativeBlock blk) {
        // dump out init action
        if (blk.initAction != null) {
            printAction(processActionForSpecialSymbols(blk.initAction, blk.getLine(), currentRule, null),blk.getLine());
        }
    }

    /** Generate the header for a block, which may be a RuleBlock or a
     * plain AlternativeBLock.  This generates any variable declarations
     * and syntactic-predicate-testing variables.
     * @param blk The block for which the preamble is to be generated.
     */
    protected void genBlockPreamble(AlternativeBlock blk) {
        // define labels for rule blocks.
        if (blk instanceof RuleBlock) {
            RuleBlock rblk = (RuleBlock)blk;
            if (rblk.labeledElements != null) {
                for (int i = 0; i < rblk.labeledElements.size(); i++) {
                    AlternativeElement a = rblk.labeledElements.get(i);
                    int oldDefaultLine = defaultLine;
                    try {
                        defaultLine = a.getLine();
                    // System.out.println("looking at labeled element: "+a);
                    // Variables for labeled rule refs and
                    // subrules are different than variables for
                    // grammar atoms.  This test is a little tricky
                    // because we want to get all rule refs and ebnf,
                    // but not rule blocks or syntactic predicates
                    if (
                        a instanceof RuleRefElement ||
                        a instanceof AlternativeBlock &&
                        !(a instanceof RuleBlock) &&
                        !(a instanceof SynPredBlock)
                    ) {

                        if (
                            !(a instanceof RuleRefElement) &&
                            ((AlternativeBlock)a).not &&
                            analyzer.subruleCanBeInverted(((AlternativeBlock)a), grammar instanceof LexerGrammar)
                        ) {
                            // Special case for inverted subrules that
                            // will be inlined.  Treat these like
                            // token or char literal references
                                println(labeledElementType + " " + a.getLabel() + " = " + labeledElementInit + ";");
                            if (grammar.buildAST) {
                                    genASTDeclaration(a);
                            }
                        }
                        else {
                            if (grammar.buildAST) {
                                // Always gen AST variables for
                                // labeled elements, even if the
                                // element itself is marked with !
                                    genASTDeclaration(a);
                            }
                            if (grammar instanceof LexerGrammar) {
                                    println("Token " + a.getLabel() + "=null;");
                            }
                            if (grammar instanceof TreeWalkerGrammar) {
                                // always generate rule-ref variables
                                // for tree walker
                                    println(labeledElementType + " " + a.getLabel() + " = " + labeledElementInit + ";");
                            }
                        }
                    }
                    else {
                        // It is a token or literal reference.  Generate the
                        // correct variable type for this grammar
                            println(labeledElementType + " " + a.getLabel() + " = " + labeledElementInit + ";");

                        // In addition, generate *_AST variables if
                        // building ASTs
                        if (grammar.buildAST) {
                            if (a instanceof GrammarAtom &&
                                ((GrammarAtom)a).getASTNodeType() != null) {
                                GrammarAtom ga = (GrammarAtom)a;
                                    genASTDeclaration(a, ga.getASTNodeType());
                            }
                            else {
                                    genASTDeclaration(a);
                            }
                        }
                    }
                    } finally {
                        defaultLine = oldDefaultLine;
                    }
                }
            }
        }
    }

    /** Generate a series of case statements that implement a BitSet test.
     * @param p The Bitset for which cases are to be generated
     * @param line the line number of the block
     */
    protected void genCases(BitSet p, int line) {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = line;
        if (DEBUG_CODE_GENERATOR) {
            System.out.println("genCases(" + p + ")");
        }
        int[] elems;

        elems = p.toArray();
        // Wrap cases four-per-line for lexer, one-per-line for parser
        int wrap = (grammar instanceof LexerGrammar) ? 4 : 1;
        int j = 1;
        boolean startOfLine = true;
        for (int i = 0; i < elems.length; i++) {
            if (j == 1) {
                    print("");
            }
            else {
                    _print("  ");
            }
                _print("case " + getValueString(elems[i]) + ":");

            if (j == wrap) {
                    _println("");
                startOfLine = true;
                j = 1;
            }
            else {
                j++;
                startOfLine = false;
            }
        }
        if (!startOfLine) {
                _println("");
            }
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /**Generate common code for a block of alternatives; return a
     * postscript that needs to be generated at the end of the
     * block.  Other routines may append else-clauses and such for
     * error checking before the postfix is generated.  If the
     * grammar is a lexer, then generate alternatives in an order
     * where alternatives requiring deeper lookahead are generated
     * first, and EOF in the lookahead set reduces the depth of
     * the lookahead.
     * @param blk The block to generate
     * @param noTestForSingle If true, then it does not generate a test
     * for a single alternative.
     * @return block finishing info
     */
    public JavaBlockFinishingInfo genCommonBlock(AlternativeBlock blk,
                                                 boolean noTestForSingle) {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = blk.getLine();
        int nIF = 0;
        boolean createdLL1Switch = false;
        int closingBracesOfIFSequence = 0;
        JavaBlockFinishingInfo finishingInfo = new JavaBlockFinishingInfo();
        if (DEBUG_CODE_GENERATOR) {
            System.out.println("genCommonBlock(" + blk + ")");
        }

        // Save the AST generation state, and set it to that of the block
        boolean savegenAST = genAST;
        genAST = genAST && blk.getAutoGen();

        boolean oldsaveTest = saveText;
        saveText = saveText && blk.getAutoGen();

        // Is this block inverted?  If so, generate special-case code
        if (
            blk.not &&
            analyzer.subruleCanBeInverted(blk, grammar instanceof LexerGrammar)
        ) {
            if (DEBUG_CODE_GENERATOR) {
                System.out.println("special case: ~(subrule)");
            }
            Lookahead p = analyzer.look(1, blk);
            // Variable assignment for labeled elements
            if (blk.getLabel() != null && syntacticPredLevel == 0) {
                    println(blk.getLabel() + " = " + lt1Value + ";");
            }

            // AST
            genElementAST(blk);

            String astArgs = "";
            if (grammar instanceof TreeWalkerGrammar) {
                astArgs = "_t,";
            }

            // match the bitset for the alternative
                println("match(" + astArgs + getBitsetName(markBitsetForGen(p.fset)) + ");");

            // tack on tree cursor motion if doing a tree walker
            if (grammar instanceof TreeWalkerGrammar) {
                    println("_t = _t.getNextSibling();");
            }
            return finishingInfo;
        }

        // Special handling for single alt
        if (blk.getAlternatives().size() == 1) {
            Alternative alt = blk.getAlternativeAt(0);
            // Generate a warning if there is a synPred for single alt.
            if (alt.synPred != null) {
                antxrTool.warning(
                    "Syntactic predicate superfluous for single alternative",
                    grammar.getFilename(),
                    blk.getAlternativeAt(0).synPred.getLine(),
                    blk.getAlternativeAt(0).synPred.getColumn()
                );
            }
            if (noTestForSingle) {
                if (alt.semPred != null) {
                    // Generate validating predicate
                    genSemPred(alt.semPred, blk.line);
                }
                genAlt(alt, blk);
                return finishingInfo;
            }
        }

        // count number of simple LL(1) cases; only do switch for
        // many LL(1) cases (no preds, no end of token refs)
        // We don't care about exit paths for (...)*, (...)+
        // because we don't explicitly have a test for them
        // as an alt in the loop.
        //
        // Also, we now count how many unicode lookahead sets
        // there are--they must be moved to DEFAULT or ELSE
        // clause.
        int nLL1 = 0;
        for (int i = 0; i < blk.getAlternatives().size(); i++) {
            Alternative a = blk.getAlternativeAt(i);
            if (JavaCodeGenerator.suitableForCaseExpression(a)) {
                nLL1++;
            }
        }

        // do LL(1) cases
        if (nLL1 >= makeSwitchThreshold) {
            // Determine the name of the item to be compared
            String testExpr = lookaheadString(1);
            createdLL1Switch = true;
            // when parsing trees, convert null to valid tree node with NULL lookahead
            if (grammar instanceof TreeWalkerGrammar) {
                    println("if (_t==null) _t=ASTNULL;");
            }
                println("switch ( " + testExpr + ") {");
            for (int i = 0; i < blk.alternatives.size(); i++) {
                Alternative alt = blk.getAlternativeAt(i);
                // ignore any non-LL(1) alts, predicated alts,
                // or end-of-token alts for case expressions
                if (!JavaCodeGenerator.suitableForCaseExpression(alt)) {
                    continue;
                }
                Lookahead p = alt.cache[1];
                if (p.fset.degree() == 0 && !p.containsEpsilon()) {
                    antxrTool.warning("Alternate omitted due to empty prediction set",
                                 grammar.getFilename(),
                                 alt.head.getLine(), alt.head.getColumn());
                }
                else {
                    genCases(p.fset, alt.head.getLine());
                    println("{", alt.head.getLine());
                    tabs++;
                    genAlt(alt, blk);
                        println("break;", JavaCodeGenerator.NO_MAPPING);
                    tabs--;
                        println("}", JavaCodeGenerator.NO_MAPPING);
                }
            }
                println("default:");
            tabs++;
        }

        // do non-LL(1) and nondeterministic cases This is tricky in
        // the lexer, because of cases like: STAR : '*' ; ASSIGN_STAR
        // : "*="; Since nextToken is generated without a loop, then
        // the STAR will have end-of-token as it's lookahead set for
        // LA(2).  So, we must generate the alternatives containing
        // trailing end-of-token in their lookahead sets *after* the
        // alternatives without end-of-token.  This implements the
        // usual lexer convention that longer matches come before
        // shorter ones, e.g.  "*=" matches ASSIGN_STAR not STAR
        //
        // For non-lexer grammars, this does not sort the alternates
        // by depth Note that alts whose lookahead is purely
        // end-of-token at k=1 end up as default or else clauses.
        int startDepth = (grammar instanceof LexerGrammar) ? grammar.maxk : 0;
        for (int altDepth = startDepth; altDepth >= 0; altDepth--) {
            if (DEBUG_CODE_GENERATOR) {
                System.out.println("checking depth " + altDepth);
            }
            for (int i = 0; i < blk.alternatives.size(); i++) {
                Alternative alt = blk.getAlternativeAt(i);
                if (DEBUG_CODE_GENERATOR) {
                    System.out.println("genAlt: " + i);
                }
                // if we made a switch above, ignore what we already took care
                // of.  Specifically, LL(1) alts with no preds
                // that do not have end-of-token in their prediction set
                // and that are not giant unicode sets.
                if (createdLL1Switch && JavaCodeGenerator.suitableForCaseExpression(alt)) {
                    if (DEBUG_CODE_GENERATOR) {
                        System.out.println("ignoring alt because it was in the switch");
                    }
                    continue;
                }
                String e;

                boolean unpredicted = false;

                if (grammar instanceof LexerGrammar) {
                    // Calculate the "effective depth" of the alt,
                    // which is the max depth at which
                    // cache[depth]!=end-of-token
                    int effectiveDepth = alt.lookaheadDepth;
                    if (effectiveDepth == GrammarAnalyzer.NONDETERMINISTIC) {
                        // use maximum lookahead
                        effectiveDepth = grammar.maxk;
                    }
                    while (effectiveDepth >= 1 &&
                        alt.cache[effectiveDepth].containsEpsilon()) {
                        effectiveDepth--;
                    }
                    // Ignore alts whose effective depth is other than
                    // the ones we are generating for this iteration.
                    if (effectiveDepth != altDepth) {
                        if (DEBUG_CODE_GENERATOR) {
                            System.out.println("ignoring alt because effectiveDepth!=altDepth;" + effectiveDepth + "!=" + altDepth);
                        }
                        continue;
                    }
                    unpredicted = lookaheadIsEmpty(alt, effectiveDepth);
                    e = getLookaheadTestExpression(alt, effectiveDepth);
                }
                else {
                    unpredicted = lookaheadIsEmpty(alt, grammar.maxk);
                    e = getLookaheadTestExpression(alt, grammar.maxk);
                }

                    int oldDefaultLine2 = defaultLine;
                    try {
                        defaultLine = alt.head.getLine();
                // Was it a big unicode range that forced unsuitability
                // for a case expression?
                if (alt.cache[1].fset.degree() > JavaCodeGenerator.caseSizeThreshold &&
                    JavaCodeGenerator.suitableForCaseExpression(alt)) {
                    if (nIF == 0) {
                                println("if " + e + " {");
                    }
                    else {
                                println("else if " + e + " {");
                    }
                }
                else if (unpredicted &&
                    alt.semPred == null &&
                    alt.synPred == null) {
                    // The alt has empty prediction set and no
                    // predicate to help out.  if we have not
                    // generated a previous if, just put {...} around
                    // the end-of-token clause
                    if (nIF == 0) {
                                println("{");
                    }
                    else {
                                println("else {");
                    }
                    finishingInfo.needAnErrorClause = false;
                }
                else { // check for sem and syn preds

                    // Add any semantic predicate expression to the
                    // lookahead test
                    if (alt.semPred != null) {
                        // if debugging, wrap the evaluation of the
                        // predicate in a method translate $ and #
                        // references
                        ActionTransInfo tInfo = new ActionTransInfo();
                        String actionStr =
                            processActionForSpecialSymbols(alt.semPred,
                                                           blk.line,
                                                           currentRule,
                                                           tInfo);
                        // ignore translation info...we don't need to
                        // do anything with it.  call that will inform
                        // SemanticPredicateListeners of the result
                        if (((grammar instanceof ParserGrammar) ||
                            (grammar instanceof LexerGrammar)) &&
                            grammar.debuggingOutput) {
                                    e = "(" + e + "&& fireSemanticPredicateEvaluated(antxr.debug.SemanticPredicateEvent.PREDICTING," +
                                addSemPred(charFormatter.escapeString(actionStr)) + "," + actionStr + "))";
                        }
                        else {
                            e = "(" + e + "&&(" + actionStr + "))";
                        }
                    }

                    // Generate any syntactic predicates
                    if (nIF > 0) {
                        if (alt.synPred != null) {
                            println("else {", alt.synPred.getLine());
                            tabs++;
                            genSynPred(alt.synPred, e);
                            closingBracesOfIFSequence++;
                        }
                        else {
                                    println("else if " + e + " {");
                        }
                    }
                    else {
                        if (alt.synPred != null) {
                            genSynPred(alt.synPred, e);
                        }
                        else {
                            // when parsing trees, convert null to
                            // valid tree node with NULL lookahead.
                            if (grammar instanceof TreeWalkerGrammar) {
                                        println("if (_t==null) _t=ASTNULL;");
                            }
                                    println("if " + e + " {");
                        }
                    }

                }
                    } finally {
                        defaultLine = oldDefaultLine2;
                    }

                nIF++;
                tabs++;
                genAlt(alt, blk);
                tabs--;
                    println("}");
            }
        }
        String ps = "";
        for (int i = 1; i <= closingBracesOfIFSequence; i++) {
            ps += "}";
        }

        // Restore the AST generation state
        genAST = savegenAST;

        // restore save text state
        saveText = oldsaveTest;

        // Return the finishing info.
        if (createdLL1Switch) {
            tabs--;
            finishingInfo.postscript = ps + "}";
            finishingInfo.generatedSwitch = true;
            finishingInfo.generatedAnIf = nIF > 0;
            //return new JavaBlockFinishingInfo(ps+"}",true,nIF>0); // close up switch statement

        }
        else {
            finishingInfo.postscript = ps;
            finishingInfo.generatedSwitch = false;
            finishingInfo.generatedAnIf = nIF > 0;
            // return new JavaBlockFinishingInfo(ps, false,nIF>0);
        }
        return finishingInfo;
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    private static boolean suitableForCaseExpression(Alternative a) {
        return
            a.lookaheadDepth == 1 &&
            a.semPred == null &&
            !a.cache[1].containsEpsilon() &&
            a.cache[1].fset.degree() <= JavaCodeGenerator.caseSizeThreshold;
    }

    /** Generate code to link an element reference into the AST
     * @param el the alternative
     */
    private void genElementAST(AlternativeElement el) {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = el.getLine();
        // handle case where you're not building trees, but are in tree walker.
        // Just need to get labels set up.
        if (grammar instanceof TreeWalkerGrammar && !grammar.buildAST) {
            String elementRef;
            String astName;

            // Generate names and declarations of the AST variable(s)
            if (el.getLabel() == null) {
                elementRef = lt1Value;
                // Generate AST variables for unlabeled stuff
                astName = "tmp" + astVarNumber + "_AST";
                astVarNumber++;
                // Map the generated AST variable in the alternate
                mapTreeVariable(el, astName);
                // Generate an "input" AST variable also
                    println(labeledElementASTType + " " + astName + "_in = " + elementRef + ";");
            }
            return;
        }

        if (grammar.buildAST && syntacticPredLevel == 0) {
            boolean needASTDecl =
                (genAST &&
                (el.getLabel() != null ||
                el.getAutoGenType() != GrammarElement.AUTO_GEN_BANG
                )
                );

            // RK: if we have a grammar element always generate the decl
            // since some guy can access it from an action and we can't
            // peek ahead (well not without making a mess).
            // I'd prefer taking this out.
            if (el.getAutoGenType() != GrammarElement.AUTO_GEN_BANG &&
                (el instanceof TokenRefElement))
            {
                needASTDecl = true;
            }

            boolean doNoGuessTest =
                (grammar.hasSyntacticPredicate && needASTDecl);

            String elementRef;
            String astNameBase;

            // Generate names and declarations of the AST variable(s)
            if (el.getLabel() != null) {
                elementRef = el.getLabel();
                astNameBase = el.getLabel();
            }
            else {
                elementRef = lt1Value;
                // Generate AST variables for unlabeled stuff
                astNameBase = "tmp" + astVarNumber;
                astVarNumber++;
            }

            // Generate the declaration if required.
            if (needASTDecl) {
                // Generate the declaration
                if (el instanceof GrammarAtom) {
                    GrammarAtom ga = (GrammarAtom)el;
                    if (ga.getASTNodeType() != null) {
                            genASTDeclaration(el, astNameBase, ga.getASTNodeType());
//						println(ga.getASTNodeType()+" " + astName+" = null;");
                    }
                    else {
                            genASTDeclaration(el, astNameBase, labeledElementASTType);
//						println(labeledElementASTType+" " + astName + " = null;");
                    }
                }
                else {
                        genASTDeclaration(el, astNameBase, labeledElementASTType);
//					println(labeledElementASTType+" " + astName + " = null;");
                }
            }

            // for convenience..
            String astName = astNameBase + "_AST";

            // Map the generated AST variable in the alternate
            mapTreeVariable(el, astName);
            if (grammar instanceof TreeWalkerGrammar) {
                // Generate an "input" AST variable also
                    println(labeledElementASTType + " " + astName + "_in = null;");
            }

            // Enclose actions with !guessing
            if (doNoGuessTest) {
                // println("if (inputState.guessing==0) {");
                // tabs++;
            }

            // if something has a label assume it will be used
            // so we must initialize the RefAST
            if (el.getLabel() != null) {
                if (el instanceof GrammarAtom) {
                        println(astName + " = " + getASTCreateString((GrammarAtom)el, elementRef) + ";");
                }
                else {
                        println(astName + " = " + getASTCreateString(elementRef) + ";");
                }
            }

            // if it has no label but a declaration exists initialize it.
            if (el.getLabel() == null && needASTDecl) {
                elementRef = lt1Value;
                if (el instanceof GrammarAtom) {
                        println(astName + " = " + getASTCreateString((GrammarAtom)el, elementRef) + ";");
                }
                else {
                        println(astName + " = " + getASTCreateString(elementRef) + ";");
                }
                // Map the generated AST variable in the alternate
                if (grammar instanceof TreeWalkerGrammar) {
                    // set "input" AST variable also
                        println(astName + "_in = " + elementRef + ";");
                }
            }

            if (genAST) {
                switch (el.getAutoGenType()) {
                    case GrammarElement.AUTO_GEN_NONE:
                            println("astFactory.addASTChild(currentAST, " + astName + ");");
                        break;
                    case GrammarElement.AUTO_GEN_CARET:
                            println("astFactory.makeASTRoot(currentAST, " + astName + ");");
                        break;
                    default:
                        break;
                }
            }
            if (doNoGuessTest) {
                // tabs--;
                // println("}");
            }
        }
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** Close the try block and generate catch phrases
     * if the element has a labeled handler in the rule
     * @param el the alternative
     */
    private void genErrorCatchForElement(AlternativeElement el) {
        if (el.getLabel() == null) {
            return;
        }
        String r = el.enclosingRuleName;
        if (grammar instanceof LexerGrammar) {
            r = CodeGenerator.encodeLexerRuleName(el.enclosingRuleName);
        }
        RuleSymbol rs = (RuleSymbol)grammar.getSymbol(r);
        if (rs == null) {
            antxrTool.fatalError("panic: Enclosing rule not found!");
            return;
        }
        ExceptionSpec ex = rs.block.findExceptionSpec(el.getLabel());
        if (ex != null) {
            tabs--;
            println("}", el.getLine());
            genErrorHandler(ex);
        }
    }

    /** Generate the catch phrases for a user-specified error handler
     * @param ex the exception specification
     */
    private void genErrorHandler(ExceptionSpec ex) {
        // Each ExceptionHandler in the ExceptionSpec is a separate catch
        for (int i = 0; i < ex.handlers.size(); i++) {
            ExceptionHandler handler = ex.handlers.get(i);
            int oldDefaultLine = defaultLine;
            try {
                defaultLine = handler.action.getLine();
            // Generate catch phrase
            println("catch (" + handler.exceptionTypeAndName.getText() + ") {", handler.exceptionTypeAndName.getLine());
            tabs++;
            if (grammar.hasSyntacticPredicate) {
                    println("if (inputState.guessing==0) {");
                tabs++;
            }

            // When not guessing, execute user handler action
            ActionTransInfo tInfo = new ActionTransInfo();
            printAction(
                processActionForSpecialSymbols(handler.action.getText(),
                                               handler.action.getLine(),
                                                   currentRule, tInfo)
            );

            if (grammar.hasSyntacticPredicate) {
                tabs--;
                    println("} else {");
                tabs++;
                // When guessing, rethrow exception
                println(
                    "throw " +
                    extractIdOfAction(handler.exceptionTypeAndName) +
                    ";"
                );
                tabs--;
                    println("}");
            }
            // Close catch phrase
            tabs--;
                println("}");
            } finally {
                defaultLine = oldDefaultLine;
            }
        }
    }

    /** Generate a try { opening if the element has a labeled handler in the rule
     * @param el the alternative
     */
    private void genErrorTryForElement(AlternativeElement el) {
        if (el.getLabel() == null) {
            return;
        }
        String r = el.enclosingRuleName;
        if (grammar instanceof LexerGrammar) {
            r = CodeGenerator.encodeLexerRuleName(el.enclosingRuleName);
        }
        RuleSymbol rs = (RuleSymbol)grammar.getSymbol(r);
        if (rs == null) {
            antxrTool.fatalError("panic: Enclosing rule not found!");
            return;
        }
        ExceptionSpec ex = rs.block.findExceptionSpec(el.getLabel());
        if (ex != null) {
            println("try { // for error handling", el.getLine());
            tabs++;
        }
    }

    protected void genASTDeclaration(AlternativeElement el) {
        genASTDeclaration(el, labeledElementASTType);
    }

    protected void genASTDeclaration(AlternativeElement el, String node_type) {
        genASTDeclaration(el, el.getLabel(), node_type);
    }

    protected void genASTDeclaration(AlternativeElement el, String var_name, String node_type) {
        // already declared?
        if (declaredASTVariables.containsValue(el)) {
            return;
        }

        // emit code
        println(node_type + " " + var_name + "_AST = null;");

        // mark as declared
        declaredASTVariables.put(el,el);
    }

    /** Generate a header that is common to all Java files */
    protected void genHeader() {
        println("// $ANTXR " + Tool.version + ": " +
                "\"" + antxrTool.fileMinusPath(antxrTool.grammarFile) + "\"" +
                " -> " +
                "\"" + grammar.getClassName() + ".java\"$", JavaCodeGenerator.NO_MAPPING);
        println("// GENERATED CODE - DO NOT EDIT!", JavaCodeGenerator.NO_MAPPING);
    }

    private void genLiteralsTest() {
        println("_ttype = testLiteralsTable(_ttype);");
    }

    private void genLiteralsTestForPartialToken() {
        println("_ttype = testLiteralsTable(new String(text.getBuffer(),_begin,text.length()-_begin),_ttype);");
    }

    protected void genMatch(BitSet b) {
        // do nothing
    }

    protected void genMatch(GrammarAtom atom) {
        if (atom instanceof StringLiteralElement) {
            if (grammar instanceof LexerGrammar) {
                genMatchUsingAtomText(atom);
            }
            else {
                genMatchUsingAtomTokenType(atom);
            }
        }
        else if (atom instanceof CharLiteralElement) {
            if (grammar instanceof LexerGrammar) {
                genMatchUsingAtomText(atom);
            }
            else {
                antxrTool.error("cannot ref character literals in grammar: " + atom);
            }
        }
        else if (atom instanceof TokenRefElement) {
            genMatchUsingAtomText(atom);
        }
        else if (atom instanceof WildcardElement) {
            gen((WildcardElement)atom);
        }
    }

    protected void genMatchUsingAtomText(GrammarAtom atom) {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = atom.getLine();
        // match() for trees needs the _t cursor
        String astArgs = "";
        if (grammar instanceof TreeWalkerGrammar) {
            astArgs = "_t,";
        }

        // if in lexer and ! on element, save buffer index to kill later
        if (grammar instanceof LexerGrammar && (!saveText || atom.getAutoGenType() == GrammarElement.AUTO_GEN_BANG)) {
                println("_saveIndex=text.length();");
        }

            print(atom.not ? "matchNot(" : "match(");
        _print(astArgs, JavaCodeGenerator.NO_MAPPING);

        // print out what to match
        if (atom.atomText.equals("EOF")) {
            // horrible hack to handle EOF case
                _print("Token.EOF_TYPE");
        }
        else {
                _print(atom.atomText);
        }
            _println(");");

        if (grammar instanceof LexerGrammar && (!saveText || atom.getAutoGenType() == GrammarElement.AUTO_GEN_BANG)) {
                println("text.setLength(_saveIndex);");		// kill text atom put in buffer
            }
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    protected void genMatchUsingAtomTokenType(GrammarAtom atom) {
        // match() for trees needs the _t cursor
        String astArgs = "";
        if (grammar instanceof TreeWalkerGrammar) {
            astArgs = "_t,";
        }

        // If the literal can be mangled, generate the symbolic constant instead
        String s = astArgs + getValueString(atom.getType());

        // matching
        println((atom.not ? "matchNot(" : "match(") + s + ");", atom.getLine());
    }

    /** Generate the nextToken() rule.  nextToken() is a synthetic
     * lexer rule that is the implicit OR of all user-defined
     * lexer rules.
     */
    public void genNextToken() {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = JavaCodeGenerator.NO_MAPPING;
        // Are there any public rules?  If not, then just generate a
        // fake nextToken().
        boolean hasPublicRules = false;
        for (RuleSymbol rs : grammar.rules) {
            if (rs.isDefined() && rs.access.equals("public")) {
                hasPublicRules = true;
                break;
            }
        }
        if (!hasPublicRules) {
                println("");
                println("public Token nextToken() throws TokenStreamException {");
                println("\ttry {uponEOF();}");
                println("\tcatch(CharStreamIOException csioe) {");
                println("\t\tthrow new TokenStreamIOException(csioe.io);");
                println("\t}");
                println("\tcatch(CharStreamException cse) {");
                println("\t\tthrow new TokenStreamException(cse.getMessage());");
                println("\t}");
                println("\treturn new CommonToken(Token.EOF_TYPE, \"\");");
                println("}");
                println("");
            return;
        }

        // Create the synthesized nextToken() rule
        RuleBlock nextTokenBlk = MakeGrammar.createNextTokenRule(grammar, grammar.rules, "nextToken");
        // Define the nextToken rule symbol
        RuleSymbol nextTokenRs = new RuleSymbol("mnextToken");
        nextTokenRs.setDefined();
        nextTokenRs.setBlock(nextTokenBlk);
        nextTokenRs.access = "private";
        grammar.define(nextTokenRs);
        // Analyze the nextToken rule
        grammar.theLLkAnalyzer.deterministic(nextTokenBlk);

        // Generate the next token rule
        String filterRule = null;
        if (((LexerGrammar)grammar).filterMode) {
            filterRule = ((LexerGrammar)grammar).filterRule;
        }

            println("");
            println("public Token nextToken() throws TokenStreamException {");
        tabs++;
            println("Token theRetToken=null;");
            _println("tryAgain:");
            println("for (;;) {");
        tabs++;
            println("Token _token = null;");
            println("int _ttype = Token.INVALID_TYPE;");
        if (((LexerGrammar)grammar).filterMode) {
                println("setCommitToPath(false);");
            if (filterRule != null) {
                // Here's a good place to ensure that the filter rule actually exists
                if (!grammar.isDefined(CodeGenerator.encodeLexerRuleName(filterRule))) {
                    grammar.antxrTool.error("Filter rule " + filterRule + " does not exist in this lexer");
                }
                else {
                    RuleSymbol rs = (RuleSymbol)grammar.getSymbol(CodeGenerator.encodeLexerRuleName(filterRule));
                    if (!rs.isDefined()) {
                        grammar.antxrTool.error("Filter rule " + filterRule + " does not exist in this lexer");
                    }
                    else if (rs.access.equals("public")) {
                        grammar.antxrTool.error("Filter rule " + filterRule + " must be protected");
                    }
                }
                    println("int _m;");
                    println("_m = mark();");
            }
        }
            println("resetText();");

            println("try {   // for char stream error handling");
        tabs++;

        // Generate try around whole thing to trap scanner errors
            println("try {   // for lexical error handling");
        tabs++;

        // Test for public lexical rules with empty paths
        for (int i = 0; i < nextTokenBlk.getAlternatives().size(); i++) {
            Alternative a = nextTokenBlk.getAlternativeAt(i);
            if (a.cache[1].containsEpsilon()) {
                //String r = a.head.toString();
                RuleRefElement rr = (RuleRefElement)a.head;
                String r = CodeGenerator.decodeLexerRuleName(rr.targetRule);
                antxrTool.warning("public lexical rule "+r+" is optional (can match \"nothing\")");
            }
        }

        // Generate the block
        String newline = System.getProperty("line.separator");
        JavaBlockFinishingInfo howToFinish = genCommonBlock(nextTokenBlk, false);
        String errFinish = "if (LA(1)==EOF_CHAR) {uponEOF(); _returnToken = makeToken(Token.EOF_TYPE);}";
        errFinish += newline + "\t\t\t\t";
        if (((LexerGrammar)grammar).filterMode) {
            if (filterRule == null) {
                errFinish += "else {consume(); continue tryAgain;}";
            }
            else {
                errFinish += "else {" + newline +
                    "\t\t\t\t\tcommit();" + newline +
                    "\t\t\t\t\ttry {m" + filterRule + "(false);}" + newline +
                    "\t\t\t\t\tcatch(RecognitionException e) {" + newline +
                    "\t\t\t\t\t	// catastrophic failure" + newline +
                    "\t\t\t\t\t	reportError(e);" + newline +
                    "\t\t\t\t\t	consume();" + newline +
                    "\t\t\t\t\t}" + newline +
                    "\t\t\t\t\tcontinue tryAgain;" + newline +
                    "\t\t\t\t}";
            }
        }
        else {
            errFinish += "else {" + throwNoViable + "}";
        }
        genBlockFinish(howToFinish, errFinish, nextTokenBlk.getLine());

        // at this point a valid token has been matched, undo "mark" that was done
        if (((LexerGrammar)grammar).filterMode && filterRule != null) {
                println("commit();");
        }

        // Generate literals test if desired
        // make sure _ttype is set first; note _returnToken must be
        // non-null as the rule was required to create it.
            println("if ( _returnToken==null ) continue tryAgain; // found SKIP token");
            println("_ttype = _returnToken.getType();");
        if (((LexerGrammar)grammar).getTestLiterals()) {
                genLiteralsTest();
        }

        // return token created by rule reference in switch
            println("_returnToken.setType(_ttype);");
            println("return _returnToken;");

        // Close try block
        tabs--;
            println("}");
            println("catch (RecognitionException e) {");
        tabs++;
        if (((LexerGrammar)grammar).filterMode) {
            if (filterRule == null) {
                    println("if ( !getCommitToPath() ) {consume(); continue tryAgain;}");
            }
            else {
                    println("if ( !getCommitToPath() ) {");
                tabs++;
                    println("rewind(_m);");
                    println("resetText();");
                    println("try {m" + filterRule + "(false);}");
                    println("catch(RecognitionException ee) {");
                    println("	// horrendous failure: error in filter rule");
                    println("	reportError(ee);");
                    println("	consume();");
                    println("}");
                    println("continue tryAgain;");
                tabs--;
                    println("}");
            }
        }
        if (nextTokenBlk.getDefaultErrorHandler()) {
                println("reportError(e);");
                println("consume();");
        }
        else {
            // pass on to invoking routine
                println("throw new TokenStreamRecognitionException(e);");
        }
        tabs--;
            println("}");

        // close CharStreamException try
        tabs--;
            println("}");
            println("catch (CharStreamException cse) {");
            println("	if ( cse instanceof CharStreamIOException ) {");
            println("		throw new TokenStreamIOException(((CharStreamIOException)cse).io);");
            println("	}");
            println("	else {");
            println("		throw new TokenStreamException(cse.getMessage());");
            println("	}");
            println("}");

        // close for-loop
        tabs--;
            println("}");

        // close method nextToken
        tabs--;
            println("}");
            println("");
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** Gen a named rule block.
     * ASTs are generated for each element of an alternative unless
     * the rule or the alternative have a '!' modifier.
     *
     * If an alternative defeats the default tree construction, it
     * must set <rule>_AST to the root of the returned AST.
     *
     * Each alternative that does automatic tree construction, builds
     * up root and child list pointers in an ASTPair structure.
     *
     * A rule finishes by setting the returnAST variable from the
     * ASTPair.
     *
     * @param s The name of the rule to generate
     * @param startSymbol true if the rule is a start symbol (i.e., not referenced elsewhere)
     * @param ruleNum the rule number
     */
    public void genRule(RuleSymbol s, boolean startSymbol, int ruleNum) {
        tabs = 1;

        if (DEBUG_CODE_GENERATOR) {
            System.out.println("genRule(" + s.getId() + ")");
        }
        if (!s.isDefined()) {
            antxrTool.error("undefined rule: " + s.getId());
            return;
        }

        // Generate rule return type, name, arguments
        RuleBlock rblk = s.getBlock();

        int oldDefaultLine = defaultLine;
        try {
            defaultLine = rblk.getLine();
        currentRule = rblk;
        currentASTResult = s.getId();

        // clear list of declared ast variables..
        declaredASTVariables.clear();

        // Save the AST generation state, and set it to that of the rule
        boolean savegenAST = genAST;
        genAST = genAST && rblk.getAutoGen();

        // boolean oldsaveTest = saveText;
        saveText = rblk.getAutoGen();

        // print javadoc comment if any
        if (s.comment != null) {
                _println(s.comment);
                }

        // Gen method access and final qualifier
            print(s.access + " final ");

        // Gen method return type (note lexer return action set at rule creation)
        if (rblk.returnAction != null) {
            // Has specified return value
                _print(extractTypeOfAction(rblk.returnAction, rblk.getLine(), rblk.getColumn()) + " ");
        }
        else {
            // No specified return value
                _print("void ");
        }

        // Gen method name
            _print(s.getId() + "(");

        // Additional rule parameters common to all rules for this grammar
            _print(commonExtraParams);
        if (commonExtraParams.length() != 0 && rblk.argAction != null) {
                _print(",");
        }

        // Gen arguments
        if (rblk.argAction != null) {
            // Has specified arguments
                _println("");
            tabs++;
                println(rblk.argAction);
            tabs--;
                print(")");
        }
        else {
            // No specified arguments
                _print(")");
        }

        // Gen throws clause and open curly
            _print(" throws " + exceptionThrown);
        if (grammar instanceof ParserGrammar) {
                _print(", TokenStreamException");
        }
        else if (grammar instanceof LexerGrammar) {
                _print(", CharStreamException, TokenStreamException");
        }
        // Add user-defined exceptions unless lexer (for now)
        if (rblk.throwsSpec != null) {
            if (grammar instanceof LexerGrammar) {
                antxrTool.error("user-defined throws spec not allowed (yet) for lexer rule " + rblk.ruleName);
            }
            else {
                    _print(", " + rblk.throwsSpec);
            }
        }

            _println(" {");
        tabs++;

        // Convert return action to variable declaration
        if (rblk.returnAction != null) {
            println(rblk.returnAction + ";");
        }

        // print out definitions needed by rules for various grammar types
            println(commonLocalVars);

        if (grammar.traceRules) {
            if (grammar instanceof TreeWalkerGrammar) {
                    println("traceIn(\"" + s.getId() + "\",_t);");
            }
            else {
                    println("traceIn(\"" + s.getId() + "\");");
            }
        }

        if (grammar instanceof LexerGrammar) {
            // lexer rule default return value is the rule's token name
            // This is a horrible hack to support the built-in EOF lexer rule.
            if (s.getId().equals("mEOF")) {
                println("_ttype = Token.EOF_TYPE;");
            } else {
                println("_ttype = " + s.getId().substring(1) + ";");
            }
                println("int _saveIndex;");		// used for element! (so we can kill text matched for element)
            /*
             println("boolean old_saveConsumedInput=saveConsumedInput;");
             if ( !rblk.getAutoGen() ) {		// turn off "save input" if ! on rule
             println("saveConsumedInput=false;");
             }
             */
        }

        // if debugging, write code to mark entry to the rule
        if (grammar.debuggingOutput) {
            if (grammar instanceof ParserGrammar) {
                println("fireEnterRule(" + ruleNum + ",0);");
            } else if (grammar instanceof LexerGrammar) {
                println("fireEnterRule(" + ruleNum + ",_ttype);");
            }
        }

        // Generate trace code if desired
        if (grammar.debuggingOutput || grammar.traceRules) {
                println("try { // debugging");
            tabs++;
        }

        // Initialize AST variables
        if (grammar instanceof TreeWalkerGrammar) {
            // "Input" value for rule
        println(labeledElementASTType + " " + s.getId() + "_AST_in = (_t == ASTNULL) ? null : (" + labeledElementASTType + ")_t;", JavaCodeGenerator.NO_MAPPING);
        }
        if (grammar.buildAST) {
            // Parser member used to pass AST returns from rule invocations
                println("returnAST = null;");
            // Tracks AST construction
            // println("ASTPair currentAST = (inputState.guessing==0) ? new ASTPair() : null;");
                println("ASTPair currentAST = new ASTPair();");
            // User-settable return value for rule.
                println(labeledElementASTType + " " + s.getId() + "_AST = null;");
        }

        genBlockPreamble(rblk);
        genBlockInitAction(rblk);
            println("");

        // Search for an unlabeled exception specification attached to the rule
        ExceptionSpec unlabeledUserSpec = rblk.findExceptionSpec("");

        // Generate try block around the entire rule for  error handling
        if (unlabeledUserSpec != null || rblk.getDefaultErrorHandler()) {
                println("try {      // for error handling");
            tabs++;
        }

        // Generate the alternatives
        if (rblk.alternatives.size() == 1) {
            // One alternative -- use simple form
            Alternative alt = rblk.getAlternativeAt(0);
            String pred = alt.semPred;
            if (pred != null) {
                genSemPred(pred, currentRule.line);
            }
            if (alt.synPred != null) {
                antxrTool.warning(
                    "Syntactic predicate ignored for single alternative",
                    grammar.getFilename(),
                    alt.synPred.getLine(),
                    alt.synPred.getColumn()
                );
            }
            genAlt(alt, rblk);
        }
        else {
            // Multiple alternatives -- generate complex form
            grammar.theLLkAnalyzer.deterministic(rblk);

            JavaBlockFinishingInfo howToFinish = genCommonBlock(rblk, false);
            genBlockFinish(howToFinish, throwNoViable, rblk.getLine());
        }

        // Generate catch phrase for error handling
        if (unlabeledUserSpec != null || rblk.getDefaultErrorHandler()) {
            // Close the try block
            tabs--;
                println("}");
        }

        // Generate user-defined or default catch phrases
        if (unlabeledUserSpec != null) {
            genErrorHandler(unlabeledUserSpec);
        }
        else if (rblk.getDefaultErrorHandler()) {
            // Generate default catch phrase
                println("catch (" + exceptionThrown + " ex) {");
            tabs++;
            // Generate code to handle error if not guessing
            if (grammar.hasSyntacticPredicate) {
                    println("if (inputState.guessing==0) {");
                tabs++;
            }
                println("reportError(ex);");
            if (!(grammar instanceof TreeWalkerGrammar)) {
                // Generate code to consume until token in k==1 follow set
                Lookahead follow = grammar.theLLkAnalyzer.FOLLOW(1, rblk.endNode);
                String followSetName = getBitsetName(markBitsetForGen(follow.fset));
                    println("recover(ex," + followSetName + ");");
            }
            else {
                // Just consume one token
                    println("if (_t!=null) {_t = _t.getNextSibling();}");
            }
            if (grammar.hasSyntacticPredicate) {
                tabs--;
                // When guessing, rethrow exception
                    println("} else {");
                    println("  throw ex;");
                    println("}");
            }
            // Close catch phrase
            tabs--;
                println("}");
        }

        // Squirrel away the AST "return" value
        if (grammar.buildAST) {
                println("returnAST = " + s.getId() + "_AST;");
        }

        // Set return tree value for tree walkers
        if (grammar instanceof TreeWalkerGrammar) {
                println("_retTree = _t;");
        }

        // Generate literals test for lexer rules so marked
        if (rblk.getTestLiterals()) {
            if (s.access.equals("protected")) {
                    genLiteralsTestForPartialToken();
            }
            else {
                    genLiteralsTest();
            }
        }

        // if doing a lexer rule, dump code to create token if necessary
        if (grammar instanceof LexerGrammar) {
                println("if ( _createToken && _token==null && _ttype!=Token.SKIP ) {");
                println("	_token = makeToken(_ttype);");
                println("	_token.setText(new String(text.getBuffer(), _begin, text.length()-_begin));");
                println("}");
                println("_returnToken = _token;");
        }

        // Gen the return statement if there is one (lexer has hard-wired return action)
        if (rblk.returnAction != null) {
                println("return " + extractIdOfAction(rblk.returnAction, rblk.getLine(), rblk.getColumn()) + ";");
        }

        if (grammar.debuggingOutput || grammar.traceRules) {
            tabs--;
                println("} finally { // debugging");
            tabs++;

            // If debugging, generate calls to mark exit of rule
            if (grammar.debuggingOutput) {
                if (grammar instanceof ParserGrammar) {
                    println("fireExitRule(" + ruleNum + ",0);");
                } else if (grammar instanceof LexerGrammar) {
                    println("fireExitRule(" + ruleNum + ",_ttype);");
                }
            }

            if (grammar.traceRules) {
                if (grammar instanceof TreeWalkerGrammar) {
                        println("traceOut(\"" + s.getId() + "\",_t);");
                }
                else {
                        println("traceOut(\"" + s.getId() + "\");");
                }
            }

            tabs--;
                println("}");
        }

        tabs--;
            println("}");
            println("");

        // Restore the AST generation state
        genAST = savegenAST;

        // restore char save state
        // saveText = oldsaveTest;
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    private void GenRuleInvocation(RuleRefElement rr) {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = rr.getLine();
        // dump rule name
        getPrintWriterManager().startSingleSourceLineMapping(rr.getLine());
            _print(rr.targetRule + "(");
        getPrintWriterManager().endMapping();

        // lexers must tell rule if it should set _returnToken
        if (grammar instanceof LexerGrammar) {
            // if labeled, could access Token, so tell rule to create
            if (rr.getLabel() != null) {
                    _print("true");
            }
            else {
                    _print("false");
            }
            if (commonExtraArgs.length() != 0 || rr.args != null) {
                    _print(",");
            }
        }

        // Extra arguments common to all rules for this grammar
            _print(commonExtraArgs);
        if (commonExtraArgs.length() != 0 && rr.args != null) {
                _print(",");
        }

        // Process arguments to method, if any
        RuleSymbol rs = (RuleSymbol)grammar.getSymbol(rr.targetRule);
        if (rr.args != null) {
            // When not guessing, execute user arg action
            ActionTransInfo tInfo = new ActionTransInfo();
            String args = processActionForSpecialSymbols(rr.args, 0, currentRule, tInfo);
            if (tInfo.assignToRoot || tInfo.refRuleRoot != null) {
                antxrTool.error("Arguments of rule reference '" + rr.targetRule + "' cannot set or ref #" +
                           currentRule.getRuleName(), grammar.getFilename(), rr.getLine(), rr.getColumn());
            }
                _print(args);

            // Warn if the rule accepts no arguments
            if (rs.block.argAction == null) {
                antxrTool.warning("Rule '" + rr.targetRule + "' accepts no arguments", grammar.getFilename(), rr.getLine(), rr.getColumn());
            }
        }
        else {
            // For C++, no warning if rule has parameters, because there may be default
            // values for all of the parameters
            if (rs.block.argAction != null) {
                antxrTool.warning("Missing parameters on reference to rule " + rr.targetRule, grammar.getFilename(), rr.getLine(), rr.getColumn());
            }
        }
            _println(");");

        // move down to the first child while parsing
        if (grammar instanceof TreeWalkerGrammar) {
                println("_t = _retTree;");
            }
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    protected void genSemPred(String pred, int line) {
        // translate $ and # references
        ActionTransInfo tInfo = new ActionTransInfo();
        pred = processActionForSpecialSymbols(pred, line, currentRule, tInfo);
        // ignore translation info...we don't need to do anything with it.
        String escapedPred = charFormatter.escapeString(pred);

        // if debugging, wrap the semantic predicate evaluation in a method
        // that can tell SemanticPredicateListeners the result
        if (grammar.debuggingOutput && ((grammar instanceof ParserGrammar) || (grammar instanceof LexerGrammar))) {
            pred = "fireSemanticPredicateEvaluated(antxr.debug.SemanticPredicateEvent.VALIDATING,"
                + addSemPred(escapedPred) + "," + pred + ")";
        }
        println("if (!(" + pred + "))", line);
        println("  throw new SemanticException(\"" + escapedPred + "\");", line);
    }

    /** Write an array of Strings which are the semantic predicate
     *  expressions.  The debugger will reference them by number only
     */
    protected void genSemPredMap() {
        println("private String _semPredNames[] = {", JavaCodeGenerator.NO_MAPPING);
        for (String semPred : semPreds) {
            println("\"" + semPred + "\",", JavaCodeGenerator.NO_MAPPING);
        }
        println("};", JavaCodeGenerator.NO_MAPPING);
    }

    protected void genSynPred(SynPredBlock blk, String lookaheadExpr) {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = blk.getLine();
        if (DEBUG_CODE_GENERATOR) {
            System.out.println("gen=>(" + blk + ")");
        }

        // Dump synpred result variable
            println("boolean synPredMatched" + blk.ID + " = false;");
        // Gen normal lookahead test
            println("if (" + lookaheadExpr + ") {");
        tabs++;

        // Save input state
        if (grammar instanceof TreeWalkerGrammar) {
                println("AST __t" + blk.ID + " = _t;");
        }
        else {
                println("int _m" + blk.ID + " = mark();");
        }

        // Once inside the try, assume synpred works unless exception caught
            println("synPredMatched" + blk.ID + " = true;");
            println("inputState.guessing++;");

        // if debugging, tell listeners that a synpred has started
        if (grammar.debuggingOutput && ((grammar instanceof ParserGrammar) ||
            (grammar instanceof LexerGrammar))) {
                println("fireSyntacticPredicateStarted();");
        }

        syntacticPredLevel++;
            println("try {");
        tabs++;
        gen(blk);		// gen code to test predicate
        tabs--;
        //println("System.out.println(\"pred "+blk+" succeeded\");");
            println("}");
            println("catch (" + exceptionThrown + " pe) {");
        tabs++;
            println("synPredMatched" + blk.ID + " = false;");
        //println("System.out.println(\"pred "+blk+" failed\");");
        tabs--;
            println("}");

        // Restore input state
        if (grammar instanceof TreeWalkerGrammar) {
                println("_t = __t" + blk.ID + ";");
        }
        else {
                println("rewind(_m" + blk.ID + ");");
        }

            _println("inputState.guessing--;");

        // if debugging, tell listeners how the synpred turned out
        if (grammar.debuggingOutput && ((grammar instanceof ParserGrammar) ||
            (grammar instanceof LexerGrammar))) {
                println("if (synPredMatched" + blk.ID + ")");
                println("  fireSyntacticPredicateSucceeded();");
                println("else");
                println("  fireSyntacticPredicateFailed();");
        }

        syntacticPredLevel--;
        tabs--;

        // Close lookahead test
            println("}");

        // Test synpred result
            println("if ( synPredMatched" + blk.ID + " ) {");
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** Generate a static array containing the names of the tokens,
     * indexed by the token type values.  This static array is used
     * to format error messages so that the token identifers or literal
     * strings are displayed instead of the token numbers.
     *
     * If a lexical rule has a paraphrase, use it rather than the
     * token label.
     */
    public void genTokenStrings() {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = JavaCodeGenerator.NO_MAPPING;
        // Generate a string for each token.  This creates a static
        // array of Strings indexed by token type.
            println("");
            println("public static final String[] _tokenNames = {");
        tabs++;

        // Walk the token vocabulary and generate a Vector of strings
        // from the tokens.
        List<String> v = grammar.tokenManager.getVocabulary();
        for (int i = 0; i < v.size(); i++) {
            String s = v.get(i);
            if (s == null) {
                s = "<" + String.valueOf(i) + ">";
            }
            if (!s.startsWith("\"") && !s.startsWith("<")) {
                TokenSymbol ts = grammar.tokenManager.getTokenSymbol(s);
                if (ts != null && ts.getParaphrase() != null) {
                    s = StringUtils.stripFrontBack(ts.getParaphrase(), "\"", "\"");
                }
            }
                print(charFormatter.literalString(s));
            if (i != v.size() - 1) {
                    _print(",");
            }
                _println("");
        }

        // Close the string array initailizer
        tabs--;
            println("};");
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** Create and set Integer token type objects that map
     *  to Java Class objects (which AST node to create).
     */
    protected void genTokenASTNodeMap() {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = JavaCodeGenerator.NO_MAPPING;
            println("");
            println("protected void buildTokenTypeASTClassMap() {");
        // Generate a map.put("T","TNode") for each token
        // if heterogeneous node known for that token T.
        tabs++;
        boolean generatedNewHashtable = false;
        int n = 0;
        // Walk the token vocabulary and generate puts.
        for (String s : grammar.tokenManager.getVocabulary()) {
            if (s != null) {
                TokenSymbol ts = grammar.tokenManager.getTokenSymbol(s);
                if (ts != null && ts.getASTNodeType() != null) {
                    n++;
                    if ( !generatedNewHashtable ) {
                        // only generate if we are going to add a mapping
                            println("tokenTypeToASTClassMap = new HashMap<Integer, Class<?>>();");
                        generatedNewHashtable = true;
                    }
                    println("tokenTypeToASTClassMap.put(new Integer("+ts.getTokenType()+"), "+
                                ts.getASTNodeType()+".class);");
                }
            }
        }

        if ( n==0 ) {
                println("tokenTypeToASTClassMap=null;");
        }
        tabs--;
            println("};");
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** Generate the token types Java file
     * @param tm the token manager
     * @throws IOException if trouble reading/writing tokens
     */
    protected void genTokenTypes(TokenManager tm) throws IOException {
        int oldDefaultLine = defaultLine;
        try {
            defaultLine = JavaCodeGenerator.NO_MAPPING;
            // Open the token output Java file and set the currentOutput stream
            // SAS: file open was moved to a method so a subclass can override
            //      This was mainly for the VAJ interface
            currentOutput = getPrintWriterManager().setupOutput(antxrTool, tm.getName() + CodeGenerator.TokenTypesFileSuffix);

        tabs = 0;

        // Generate the header common to all Java files
        genHeader();
        // Do not use printAction because we assume tabs==0
            try {
                defaultLine = behavior.getHeaderActionLine("");
                println(behavior.getHeaderAction(""));
            } finally {
                defaultLine = JavaCodeGenerator.NO_MAPPING;
            }

        // Encapsulate the definitions in an interface.  This can be done
        // because they are all constants.
            println("@SuppressWarnings(\"all\")");
            println("public interface " + tm.getName() + CodeGenerator.TokenTypesFileSuffix + " {");
        tabs++;

        // Generate a definition for each token type
        List<String> v = tm.getVocabulary();

        // Do special tokens manually
            println("int EOF = " + Token.EOF_TYPE + ";");
            println("int NULL_TREE_LOOKAHEAD = " + Token.NULL_TREE_LOOKAHEAD + ";");

        for (int i = Token.MIN_USER_TYPE; i < v.size(); i++) {
            String s = v.get(i);
            if (s != null) {
                if (s.startsWith("\"")) {
                    // a string literal
                    StringLiteralSymbol sl = (StringLiteralSymbol)tm.getTokenSymbol(s);
                    if (sl == null) {
                        antxrTool.fatalError("panic: String literal " + s + " not in symbol table");
                    }
                    else if (sl.label != null) {
                            println("int " + sl.label + " = " + i + ";");
                    }
                    else {
                        String mangledName = mangleLiteral(s);
                        if (mangledName != null) {
                            // We were able to create a meaningful mangled token name
                                println("int " + mangledName + " = " + i + ";");
                            // if no label specified, make the label equal to the mangled name
                            sl.label = mangledName;
                        }
                        else {
                                println("// " + s + " = " + i);
                        }
                    }
                }
                else if (!s.startsWith("<")) {
                        println("int " + s + " = " + i + ";");
                }
            }
        }

        // Close the interface
        tabs--;
            println("}");

        // Close the tokens output file
        getPrintWriterManager().finishOutput();
        exitIfError();
        } finally {
            defaultLine = oldDefaultLine;
        }
    }

    /** Get a string for an expression to generate creation of an AST subtree.
     * @param v A Vector of String, where each element is an expression in the target language yielding an AST node.
     * @return code to create the AST
     */
    @Override
    public String getASTCreateString(List<String> v) {
        if (v.size() == 0) {
            return "";
        }
        StringBuffer buf = new StringBuffer();
        buf.append("(" + labeledElementASTType +
                   ")astFactory.make( (new ASTArray(" + v.size() +
                   "))");
        for (int i = 0; i < v.size(); i++) {
            buf.append(".add(" + v.get(i) + ")");
        }
        buf.append(")");
        return buf.toString();
    }

    /** Get a string for an expression to generate creating of an AST node
     * @param atom The grammar node for which you are creating the node
     * @param astCtorArgs The arguments to the AST constructor
     * @return code to create the AST
     */
    @Override
    public String getASTCreateString(GrammarAtom atom, String astCtorArgs) {
        //System.out.println("getASTCreateString("+atom+","+astCtorArgs+")");
        if (atom != null && atom.getASTNodeType() != null) {
            // they specified a type either on the reference or in tokens{} section
            return "("+atom.getASTNodeType()+")"+
                    "astFactory.create("+astCtorArgs+",\""+atom.getASTNodeType()+"\")";
        }
        // must be an action or something since not referencing an atom
        return getASTCreateString(astCtorArgs);
    }

    /** Get a string for an expression to generate creating of an AST node.
     *  Parse the first (possibly only) argument looking for the token type.
     *  If the token type is a valid token symbol, ask for it's AST node type
     *  and add to the end if only 2 arguments.  The forms are #[T], #[T,"t"],
     *  and as of 2.7.2 #[T,"t",ASTclassname].
     *
     * @param astCtorArgs The arguments to the AST constructor
     * @return code to create the AST
     */
    public String getASTCreateString(String astCtorArgs) {
        //System.out.println("AST CTOR: "+astCtorArgs);
        if ( astCtorArgs==null ) {
            astCtorArgs = "";
        }
        int nCommas = 0;
        for (int i=0; i<astCtorArgs.length(); i++) {
            if ( astCtorArgs.charAt(i)==',' ) {
                nCommas++;
            }
        }
        //System.out.println("num commas="+nCommas);
        if ( nCommas<2 ) { // if 1 or 2 args
            int firstComma = astCtorArgs.indexOf(',');
            String tokenName = astCtorArgs;
            if ( nCommas>0 ) {
                tokenName = astCtorArgs.substring(0,firstComma);
            }
            //System.out.println("Checking for ast node type of "+tokenName);
            TokenSymbol ts = grammar.tokenManager.getTokenSymbol(tokenName);
            if ( ts!=null ) {
                String astNodeType = ts.getASTNodeType();
                //System.out.println("node type of "+tokenName+" is "+astNodeType);
                String emptyText = "";
                if ( nCommas==0 ) {
                    // need to add 2nd arg of blank text for token text
                    emptyText = ",\"\"";
                }
                if ( astNodeType!=null ) {
                    return "("+astNodeType+")"+
                            "astFactory.create("+astCtorArgs+emptyText+",\""+astNodeType+"\")";
                }
                // fall through and just do a regular create with cast on front
                // if necessary (it differs from default "AST").
            }
            if ( labeledElementASTType.equals("AST") ) {
                return "astFactory.create("+astCtorArgs+")";
            }
            return "("+labeledElementASTType+")"+
                    "astFactory.create("+astCtorArgs+")";
        }
        // create default type or (since 2.7.2) 3rd arg is classname
        return "(" + labeledElementASTType + ")astFactory.create(" + astCtorArgs + ")";
    }

    protected String getLookaheadTestExpression(Lookahead[] look, int k) {
        StringBuffer e = new StringBuffer(100);
        boolean first = true;

        e.append("(");
        for (int i = 1; i <= k; i++) {
            BitSet p = look[i].fset;
            if (!first) {
                e.append(") && (");
            }
            first = false;

            // Syn preds can yield <end-of-syn-pred> (epsilon) lookahead.
            // There is no way to predict what that token would be.  Just
            // allow anything instead.
            if (look[i].containsEpsilon()) {
                e.append("true");
            }
            else {
                e.append(getLookaheadTestTerm(i, p));
            }
        }
        e.append(")");

        return e.toString();
    }

    /**Generate a lookahead test expression for an alternate.  This
     * will be a series of tests joined by '&&' and enclosed by '()',
     * the number of such tests being determined by the depth of the lookahead.
     * @param alt the alternative
     * @param maxDepth max depth for lookahead
     * @return the lookahead test expression
     */
    protected String getLookaheadTestExpression(Alternative alt, int maxDepth) {
        int depth = alt.lookaheadDepth;
        if (depth == GrammarAnalyzer.NONDETERMINISTIC) {
            // if the decision is nondeterministic, do the best we can: LL(k)
            // any predicates that are around will be generated later.
            depth = grammar.maxk;
        }

        if (maxDepth == 0) {
            // empty lookahead can result from alt with sem pred
            // that can see end of token.  E.g., A : {pred}? ('a')? ;
            return "( true )";
        }

        return "(" + getLookaheadTestExpression(alt.cache, depth) + ")";
    }

    /**Generate a depth==1 lookahead test expression given the BitSet.
     * This may be one of:
     * 1) a series of 'x==X||' tests
     * 2) a range test using >= && <= where possible,
     * 3) a bitset membership test for complex comparisons
     * @param k The lookahead level
     * @param p The lookahead set for level k
     * @return the lookahead test term code
     */
    protected String getLookaheadTestTerm(int k, BitSet p) {
        // Determine the name of the item to be compared
        String ts = lookaheadString(k);

        // Generate a range expression if possible
        int[] elems = p.toArray();
        if (CodeGenerator.elementsAreRange(elems)) {
            return getRangeExpression(k, elems);
        }

        // Generate a bitset membership test if possible
        StringBuffer e;
        int degree = p.degree();
        if (degree == 0) {
            return "true";
        }

        if (degree >= bitsetTestThreshold) {
            int bitsetIdx = markBitsetForGen(p);
            return getBitsetName(bitsetIdx) + ".member(" + ts + ")";
        }

        // Otherwise, generate the long-winded series of "x==X||" tests
        e = new StringBuffer();
        for (int i = 0; i < elems.length; i++) {
            // Get the compared-to item (token or character value)
            String cs = getValueString(elems[i]);

            // Generate the element comparison
            if (i > 0) {
                e.append("||");
            }
            e.append(ts);
            e.append("==");
            e.append(cs);
        }
        return e.toString();
    }

    /** Return an expression for testing a contiguous renage of elements
     * @param k The lookahead level
     * @param elems The elements representing the set, usually from BitSet.toArray().
     * @return String containing test expression.
     */
    public String getRangeExpression(int k, int[] elems) {
        if (!CodeGenerator.elementsAreRange(elems)) {
            antxrTool.fatalError("panic: getRangeExpression called with non-range");
        }
        int begin = elems[0];
        int end = elems[elems.length - 1];
        return
            "(" + lookaheadString(k) + " >= " + getValueString(begin) + " && " +
            lookaheadString(k) + " <= " + getValueString(end) + ")";
    }

    /** getValueString: get a string representation of a token or char value
     * @param value The token or char value
     * @return the string representing the value
     */
    private String getValueString(int value) {
        String cs;
        if (grammar instanceof LexerGrammar) {
            cs = charFormatter.literalChar(value);
        }
        else {
            TokenSymbol ts = grammar.tokenManager.getTokenSymbolAt(value);
            if (ts == null) {
                return "" + value; // return token type as string
                // tool.fatalError("panic: vocabulary for token type " + value + " is null");
            }
            String tId = ts.getId();
            if (ts instanceof StringLiteralSymbol) {
                // if string literal, use predefined label if any
                // if no predefined, try to mangle into LITERAL_xxx.
                // if can't mangle, use int value as last resort
                StringLiteralSymbol sl = (StringLiteralSymbol)ts;
                String label = sl.getLabel();
                if (label != null) {
                    cs = label;
                }
                else {
                    cs = mangleLiteral(tId);
                    if (cs == null) {
                        cs = String.valueOf(value);
                    }
                }
            }
            else {
                cs = tId;
            }
        }
        return cs;
    }

    /**Is the lookahead for this alt empty?
     * @param alt the alternative
     * @param maxDepth the max lookahead
     * @return true if lookahead is empty
     */
    protected boolean lookaheadIsEmpty(Alternative alt, int maxDepth) {
        int depth = alt.lookaheadDepth;
        if (depth == GrammarAnalyzer.NONDETERMINISTIC) {
            depth = grammar.maxk;
        }
        for (int i = 1; i <= depth && i <= maxDepth; i++) {
            BitSet p = alt.cache[i].fset;
            if (p.degree() != 0) {
                return false;
            }
        }
        return true;
    }

    private String lookaheadString(int k) {
        if (grammar instanceof TreeWalkerGrammar) {
            return "_t.getType()";
        }
        return "LA(" + k + ")";
    }

    /** Mangle a string literal into a meaningful token name.  This is
     * only possible for literals that are all characters.  The resulting
     * mangled literal name is literalsPrefix with the text of the literal
     * appended.
     * @param s the literal to mangle
     * @return A string representing the mangled literal, or null if not possible.
     */
    private String mangleLiteral(String s) {
        String mangled = antxrTool.literalsPrefix;
        for (int i = 1; i < s.length() - 1; i++) {
            if (!Character.isLetter(s.charAt(i)) &&
                s.charAt(i) != '_') {
                return null;
            }
            mangled += s.charAt(i);
        }
        if (antxrTool.upperCaseMangledLiterals) {
            mangled = mangled.toUpperCase();
        }
        return mangled;
    }

    /** Map an identifier to it's corresponding tree-node variable.
     * This is context-sensitive, depending on the rule and alternative
     * being generated
     * @param idParam The identifier name to map
     * @param transInfo transInfo
     * @return The mapped id (which may be the same as the input), or null if the mapping is invalid due to duplicates
     */
    @Override
    public String mapTreeId(String idParam, ActionTransInfo transInfo) {
        // if not in an action of a rule, nothing to map.
        if (currentRule == null) {
            return idParam;
        }

        boolean in_var = false;
        String id = idParam;
        if (grammar instanceof TreeWalkerGrammar) {
            if (!grammar.buildAST) {
                in_var = true;
            }
            // If the id ends with "_in", then map it to the input variable
            else if (id.length() > 3 && id.lastIndexOf("_in") == id.length() - 3) {
                // Strip off the "_in"
                id = id.substring(0, id.length() - 3);
                in_var = true;
            }
        }

        // Check the rule labels.  If id is a label, then the output
        // variable is label_AST, and the input variable is plain label.
        for (AlternativeElement elt : currentRule.labeledElements) {
            if (elt.getLabel().equals(id)) {
                return in_var ? id : id + "_AST";
            }
        }

        // Failing that, check the id-to-variable map for the alternative.
        // If the id is in the map, then output variable is the name in the
        // map, and input variable is name_in
        String s = treeVariableMap.get(id);
        if (s != null) {
            if (s == JavaCodeGenerator.NONUNIQUE) {
                // There is more than one element with this id
                antxrTool.error("Ambiguous reference to AST element "+id+
                                " in rule "+currentRule.getRuleName());

                return null;
            }
            else if (s.equals(currentRule.getRuleName())) {
                // a recursive call to the enclosing rule is
                // ambiguous with the rule itself.
                antxrTool.error("Ambiguous reference to AST element "+id+
                                " in rule "+currentRule.getRuleName());
                return null;
            }
            else {
                return in_var ? s + "_in" : s;
            }
        }

        // Failing that, check the rule name itself.  Output variable
        // is rule_AST; input variable is rule_AST_in (treeparsers).
        if (id.equals(currentRule.getRuleName())) {
            String r = in_var ? id + "_AST_in" : id + "_AST";
            if (transInfo != null) {
                if (!in_var) {
                    transInfo.refRuleRoot = r;
                }
            }
            return r;
        }
        // id does not map to anything -- return itself.
        return id;
    }

    /** Given an element and the name of an associated AST variable,
     * create a mapping between the element "name" and the variable name.
     * @param e the alternative
     * @param name the name
     */
    private void mapTreeVariable(AlternativeElement e, String name) {
        // For tree elements, defer to the root
        if (e instanceof TreeElement) {
            mapTreeVariable(((TreeElement)e).root, name);
            return;
        }

        // Determine the name of the element, if any, for mapping purposes
        String elName = null;

        // Don't map labeled items
        if (e.getLabel() == null) {
            if (e instanceof TokenRefElement) {
                // use the token id
                elName = ((TokenRefElement)e).atomText;
            }
            else if (e instanceof RuleRefElement) {
                // use the rule name
                elName = ((RuleRefElement)e).targetRule;
            }
        }
        // Add the element to the tree variable map if it has a name
        if (elName != null) {
            if (treeVariableMap.get(elName) != null) {
                // Name is already in the map -- mark it as duplicate
                treeVariableMap.remove(elName);
                treeVariableMap.put(elName, JavaCodeGenerator.NONUNIQUE);
            }
            else {
                treeVariableMap.put(elName, name);
            }
        }
    }

    /** Lexically process $var and tree-specifiers in the action.
     *  This will replace #id and #(...) with the appropriate
     *  function calls and/or variables etc...
     * @param actionStr the string to process
     * @param line the line number where the action code starts
     * @param currentRule the current rule
     * @param tInfo transInfo
     * @return the processed action string
     */
    @Override
    protected String processActionForSpecialSymbols(String actionStr,
                                                    int line,
                                                    RuleBlock theCurrentRule,
                                                    ActionTransInfo tInfo) {
        if (actionStr == null || actionStr.length() == 0) {
            return null;
        }

        // The action trans info tells us (at the moment) whether an
        // assignment was done to the rule's tree root.
        if (grammar == null) {
            return actionStr;
        }

        // see if we have anything to do...
        if ((grammar.buildAST && actionStr.indexOf('#') != -1) ||
            (actionStr.indexOf('@') != -1) ||
            grammar instanceof TreeWalkerGrammar ||
            ((grammar instanceof LexerGrammar ||
            grammar instanceof ParserGrammar)
            && actionStr.indexOf('$') != -1)) {
            // Create a lexer to read an action and return the translated version
            ActionLexer lexer =
                    new ActionLexer(actionStr,theCurrentRule,this,tInfo);

            lexer.setLineOffset(line);
            lexer.setFilename(grammar.getFilename());
            lexer.setTool(antxrTool);

            try {
                lexer.mACTION(true);
                actionStr = lexer.getTokenObject().getText();
                // System.out.println("action translated: "+actionStr);
                    // System.out.println("trans info is "+tInfo);
            }
            catch (RecognitionException ex) {
                lexer.reportError(ex);
                return actionStr;
            }
            catch (TokenStreamException tex) {
                antxrTool.fatalError("panic: Error reading action:" + actionStr);
                return actionStr;
            }
            catch (CharStreamException io) {
                antxrTool.fatalError("panic: Error reading action:" + actionStr);
                return actionStr;
            }
        }
        return actionStr;
    }

    private void setupGrammarParameters(Grammar g) {
        if (g instanceof ParserGrammar) {
            labeledElementASTType = "AST";
            if (g.hasOption("ASTLabelType")) {
                Token tsuffix = g.getOption("ASTLabelType");
                if (tsuffix != null) {
                    String suffix = StringUtils.stripFrontBack(tsuffix.getText(), "\"", "\"");
                    if (suffix != null) {
                        labeledElementASTType = suffix;
                    }
                }
            }
            labeledElementType = "Token ";
            labeledElementInit = "null";
            commonExtraArgs = "";
            commonExtraParams = "";
            commonLocalVars = "";
            lt1Value = "LT(1)";
            exceptionThrown = "RecognitionException";
            throwNoViable = "throw new NoViableAltException(LT(1), getFilename());";
        }
        else if (g instanceof LexerGrammar) {
            labeledElementType = "char ";
            labeledElementInit = "'\\0'";
            commonExtraArgs = "";
            commonExtraParams = "boolean _createToken";
            commonLocalVars = "int _ttype; Token _token=null; int _begin=text.length();";
            lt1Value = "LA(1)";
            exceptionThrown = "RecognitionException";
            throwNoViable = "throw new NoViableAltForCharException(LA(1), getFilename(), getLine(), getColumn());";
        }
        else if (g instanceof TreeWalkerGrammar) {
            labeledElementASTType = "AST";
            labeledElementType = "AST";
            if (g.hasOption("ASTLabelType")) {
                Token tsuffix = g.getOption("ASTLabelType");
                if (tsuffix != null) {
                    String suffix = StringUtils.stripFrontBack(tsuffix.getText(), "\"", "\"");
                    if (suffix != null) {
                        labeledElementASTType = suffix;
                        labeledElementType = suffix;
                    }
                }
            }
            if (!g.hasOption("ASTLabelType")) {
                g.setOption("ASTLabelType", new Token(ANTXRTokenTypes.STRING_LITERAL, "AST"));
            }
            labeledElementInit = "null";
            commonExtraArgs = "_t";
            commonExtraParams = "AST _t";
            commonLocalVars = "";
            lt1Value = "(" + labeledElementASTType + ")_t";
            exceptionThrown = "RecognitionException";
            throwNoViable = "throw new NoViableAltException(_t);";
        }
        else {
            antxrTool.fatalError("panic: Unknown grammar type");
        }
    }

    /**
     * Get the printwriter manager that manages output
     * @return The print writer manager
     */
    public JavaCodeGeneratorPrintWriterManager getPrintWriterManager() {
        if (printWriterManager == null) {
            printWriterManager = new DefaultJavaCodeGeneratorPrintWriterManager();
        }
        return printWriterManager;
    }

    /**
     * Set the print writer manager
     * @param printWriterManager the new manager
     */
    public void setPrintWriterManager(JavaCodeGeneratorPrintWriterManager printWriterManager) {
        this.printWriterManager = printWriterManager;
    }

    /** {@inheritDoc} */
    @Override
    public void setTool(Tool tool) {
        super.setTool(tool);
    }
}
